typeclaw 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -0
  3. package/auth.schema.json +63 -0
  4. package/cron.schema.json +96 -0
  5. package/package.json +72 -0
  6. package/scripts/emit-base-dockerfile.ts +5 -0
  7. package/scripts/generate-schema.ts +34 -0
  8. package/secrets.schema.json +63 -0
  9. package/src/agent/auth.ts +119 -0
  10. package/src/agent/compaction.ts +35 -0
  11. package/src/agent/git-nudge.ts +95 -0
  12. package/src/agent/index.ts +451 -0
  13. package/src/agent/plugin-tools.ts +269 -0
  14. package/src/agent/reload-tool.ts +71 -0
  15. package/src/agent/self.ts +45 -0
  16. package/src/agent/session-origin.ts +288 -0
  17. package/src/agent/subagents.ts +253 -0
  18. package/src/agent/system-prompt.ts +68 -0
  19. package/src/agent/tools/channel-fetch-attachment.ts +118 -0
  20. package/src/agent/tools/channel-history.ts +119 -0
  21. package/src/agent/tools/channel-reply.ts +182 -0
  22. package/src/agent/tools/channel-send.ts +212 -0
  23. package/src/agent/tools/ddg.ts +218 -0
  24. package/src/agent/tools/restart.ts +122 -0
  25. package/src/agent/tools/stream-snapshot.ts +181 -0
  26. package/src/agent/tools/webfetch/fetch.ts +102 -0
  27. package/src/agent/tools/webfetch/index.ts +1 -0
  28. package/src/agent/tools/webfetch/strategies/grep.ts +70 -0
  29. package/src/agent/tools/webfetch/strategies/jq.ts +31 -0
  30. package/src/agent/tools/webfetch/strategies/raw.ts +3 -0
  31. package/src/agent/tools/webfetch/strategies/readability.ts +30 -0
  32. package/src/agent/tools/webfetch/strategies/selector.ts +41 -0
  33. package/src/agent/tools/webfetch/strategies/snapshot.ts +135 -0
  34. package/src/agent/tools/webfetch/tool.ts +281 -0
  35. package/src/agent/tools/webfetch/types.ts +33 -0
  36. package/src/agent/tools/websearch.ts +96 -0
  37. package/src/agent/tools/wikipedia.ts +52 -0
  38. package/src/bundled-plugins/agent-browser/dashboard-discovery.ts +170 -0
  39. package/src/bundled-plugins/agent-browser/dashboard-proxy.ts +421 -0
  40. package/src/bundled-plugins/agent-browser/index.ts +179 -0
  41. package/src/bundled-plugins/agent-browser/shim-install.ts +158 -0
  42. package/src/bundled-plugins/agent-browser/shim.ts +152 -0
  43. package/src/bundled-plugins/agent-browser/skills/agent-browser/SKILL.md +113 -0
  44. package/src/bundled-plugins/guard/index.ts +26 -0
  45. package/src/bundled-plugins/guard/policies/non-workspace-write.ts +98 -0
  46. package/src/bundled-plugins/guard/policies/skill-authoring.ts +185 -0
  47. package/src/bundled-plugins/guard/policies/uncommitted-changes.ts +85 -0
  48. package/src/bundled-plugins/guard/policy.ts +18 -0
  49. package/src/bundled-plugins/memory/README.md +71 -0
  50. package/src/bundled-plugins/memory/append-tool.ts +84 -0
  51. package/src/bundled-plugins/memory/dreaming-state.ts +86 -0
  52. package/src/bundled-plugins/memory/dreaming.ts +470 -0
  53. package/src/bundled-plugins/memory/fragment-parser.ts +67 -0
  54. package/src/bundled-plugins/memory/index.ts +238 -0
  55. package/src/bundled-plugins/memory/load-memory.ts +122 -0
  56. package/src/bundled-plugins/memory/memory-logger.ts +257 -0
  57. package/src/bundled-plugins/memory/secret-detector.ts +49 -0
  58. package/src/bundled-plugins/memory/watermark.ts +15 -0
  59. package/src/bundled-plugins/security/index.ts +35 -0
  60. package/src/bundled-plugins/security/policies/git-exfil.ts +120 -0
  61. package/src/bundled-plugins/security/policies/outbound-secret-scan.ts +167 -0
  62. package/src/bundled-plugins/security/policies/prompt-injection.ts +488 -0
  63. package/src/bundled-plugins/security/policies/secret-exfil-bash.ts +99 -0
  64. package/src/bundled-plugins/security/policies/secret-exfil-read.ts +127 -0
  65. package/src/bundled-plugins/security/policies/session-search-secrets.ts +86 -0
  66. package/src/bundled-plugins/security/policies/ssrf.ts +196 -0
  67. package/src/bundled-plugins/security/policies/system-prompt-leak.ts +81 -0
  68. package/src/bundled-plugins/security/policy.ts +9 -0
  69. package/src/channels/adapters/discord-bot-channel-resolver.ts +77 -0
  70. package/src/channels/adapters/discord-bot-classify.ts +148 -0
  71. package/src/channels/adapters/discord-bot.ts +640 -0
  72. package/src/channels/adapters/kakaotalk-author-resolver.ts +78 -0
  73. package/src/channels/adapters/kakaotalk-channel-resolver.ts +105 -0
  74. package/src/channels/adapters/kakaotalk-classify.ts +77 -0
  75. package/src/channels/adapters/kakaotalk.ts +622 -0
  76. package/src/channels/adapters/slack-bot-author-resolver.ts +80 -0
  77. package/src/channels/adapters/slack-bot-channel-resolver.ts +84 -0
  78. package/src/channels/adapters/slack-bot-classify.ts +213 -0
  79. package/src/channels/adapters/slack-bot-dedupe.ts +51 -0
  80. package/src/channels/adapters/slack-bot-time.ts +10 -0
  81. package/src/channels/adapters/slack-bot.ts +881 -0
  82. package/src/channels/adapters/telegram-bot-classify.ts +155 -0
  83. package/src/channels/adapters/telegram-bot-format.ts +309 -0
  84. package/src/channels/adapters/telegram-bot.ts +604 -0
  85. package/src/channels/engagement.ts +227 -0
  86. package/src/channels/index.ts +21 -0
  87. package/src/channels/manager.ts +292 -0
  88. package/src/channels/membership-cache.ts +116 -0
  89. package/src/channels/membership-from-history.ts +53 -0
  90. package/src/channels/membership.ts +30 -0
  91. package/src/channels/participants.ts +47 -0
  92. package/src/channels/persistence.ts +209 -0
  93. package/src/channels/reloadable.ts +28 -0
  94. package/src/channels/router.ts +1570 -0
  95. package/src/channels/schema.ts +273 -0
  96. package/src/channels/types.ts +160 -0
  97. package/src/cli/channel.ts +403 -0
  98. package/src/cli/compose-status.ts +95 -0
  99. package/src/cli/compose.ts +240 -0
  100. package/src/cli/hostd.ts +163 -0
  101. package/src/cli/index.ts +27 -0
  102. package/src/cli/init.ts +592 -0
  103. package/src/cli/logs.ts +38 -0
  104. package/src/cli/reload.ts +68 -0
  105. package/src/cli/restart.ts +66 -0
  106. package/src/cli/run.ts +77 -0
  107. package/src/cli/shell.ts +33 -0
  108. package/src/cli/start.ts +57 -0
  109. package/src/cli/status.ts +178 -0
  110. package/src/cli/stop.ts +31 -0
  111. package/src/cli/tui.ts +35 -0
  112. package/src/cli/ui.ts +110 -0
  113. package/src/commands/index.ts +74 -0
  114. package/src/compose/discover.ts +43 -0
  115. package/src/compose/index.ts +25 -0
  116. package/src/compose/logs.ts +162 -0
  117. package/src/compose/restart.ts +69 -0
  118. package/src/compose/start.ts +62 -0
  119. package/src/compose/status.ts +28 -0
  120. package/src/compose/stop.ts +43 -0
  121. package/src/config/config.ts +424 -0
  122. package/src/config/index.ts +25 -0
  123. package/src/config/providers.ts +234 -0
  124. package/src/config/reloadable.ts +47 -0
  125. package/src/container/index.ts +27 -0
  126. package/src/container/logs.ts +37 -0
  127. package/src/container/port.ts +137 -0
  128. package/src/container/shared.ts +290 -0
  129. package/src/container/shell.ts +58 -0
  130. package/src/container/start.ts +670 -0
  131. package/src/container/status.ts +76 -0
  132. package/src/container/stop.ts +120 -0
  133. package/src/container/verify-running.ts +149 -0
  134. package/src/cron/consumer.ts +138 -0
  135. package/src/cron/index.ts +54 -0
  136. package/src/cron/reloadable.ts +64 -0
  137. package/src/cron/scheduler.ts +200 -0
  138. package/src/cron/schema.ts +96 -0
  139. package/src/hostd/client.ts +113 -0
  140. package/src/hostd/daemon.ts +587 -0
  141. package/src/hostd/index.ts +25 -0
  142. package/src/hostd/paths.ts +82 -0
  143. package/src/hostd/portbroker-manager.ts +101 -0
  144. package/src/hostd/protocol.ts +48 -0
  145. package/src/hostd/spawn.ts +224 -0
  146. package/src/hostd/supervisor.ts +60 -0
  147. package/src/hostd/tailscale.ts +172 -0
  148. package/src/hostd/version.ts +115 -0
  149. package/src/init/dockerfile.ts +327 -0
  150. package/src/init/ensure-deps.ts +152 -0
  151. package/src/init/gitignore.ts +46 -0
  152. package/src/init/hatching.ts +60 -0
  153. package/src/init/index.ts +786 -0
  154. package/src/init/kakaotalk-auth.ts +114 -0
  155. package/src/init/models-dev.ts +130 -0
  156. package/src/init/oauth-login.ts +74 -0
  157. package/src/init/packagejson.ts +94 -0
  158. package/src/init/paths.ts +2 -0
  159. package/src/init/run-bun-install.ts +20 -0
  160. package/src/markdown/chunk.ts +299 -0
  161. package/src/markdown/index.ts +1 -0
  162. package/src/plugin/context.ts +40 -0
  163. package/src/plugin/define.ts +35 -0
  164. package/src/plugin/hooks.ts +204 -0
  165. package/src/plugin/index.ts +63 -0
  166. package/src/plugin/loader.ts +111 -0
  167. package/src/plugin/manager.ts +136 -0
  168. package/src/plugin/registry.ts +145 -0
  169. package/src/plugin/skills.ts +62 -0
  170. package/src/plugin/types.ts +172 -0
  171. package/src/portbroker/bind-with-forward.ts +102 -0
  172. package/src/portbroker/container-server.ts +305 -0
  173. package/src/portbroker/forward-result-bus.ts +36 -0
  174. package/src/portbroker/hostd-client.ts +443 -0
  175. package/src/portbroker/index.ts +33 -0
  176. package/src/portbroker/policy.ts +24 -0
  177. package/src/portbroker/proc-net-tcp.ts +72 -0
  178. package/src/portbroker/protocol.ts +39 -0
  179. package/src/reload/client.ts +59 -0
  180. package/src/reload/index.ts +3 -0
  181. package/src/reload/registry.ts +60 -0
  182. package/src/reload/types.ts +13 -0
  183. package/src/run/bundled-plugins.ts +24 -0
  184. package/src/run/channel-session-factory.ts +105 -0
  185. package/src/run/index.ts +432 -0
  186. package/src/run/plugin-runtime.ts +43 -0
  187. package/src/run/schema-with-plugins.ts +14 -0
  188. package/src/secrets/index.ts +13 -0
  189. package/src/secrets/migrate.ts +95 -0
  190. package/src/secrets/schema.ts +75 -0
  191. package/src/secrets/storage.ts +231 -0
  192. package/src/server/index.ts +436 -0
  193. package/src/sessions/index.ts +23 -0
  194. package/src/shared/index.ts +9 -0
  195. package/src/shared/local-time.ts +21 -0
  196. package/src/shared/protocol.ts +25 -0
  197. package/src/skills/typeclaw-channel-kakaotalk/SKILL.md +87 -0
  198. package/src/skills/typeclaw-channel-telegram-bot/SKILL.md +64 -0
  199. package/src/skills/typeclaw-config/SKILL.md +643 -0
  200. package/src/skills/typeclaw-cron/SKILL.md +159 -0
  201. package/src/skills/typeclaw-git/SKILL.md +89 -0
  202. package/src/skills/typeclaw-memory/SKILL.md +174 -0
  203. package/src/skills/typeclaw-monorepo/SKILL.md +175 -0
  204. package/src/skills/typeclaw-plugins/SKILL.md +594 -0
  205. package/src/skills/typeclaw-skills/SKILL.md +246 -0
  206. package/src/stream/broker.ts +161 -0
  207. package/src/stream/index.ts +16 -0
  208. package/src/stream/types.ts +69 -0
  209. package/src/tui/client.ts +45 -0
  210. package/src/tui/format.ts +317 -0
  211. package/src/tui/index.ts +225 -0
  212. package/src/tui/theme.ts +41 -0
  213. package/typeclaw.schema.json +826 -0
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: typeclaw-cron
3
+ description: Use this skill whenever the user asks you to schedule recurring work, run something on a cron, do something every day/hour/week, set up a periodic task, or read or edit your cron schedule. Triggers include "every morning", "every Monday", "schedule a", "remind me every", "set up a cron", "run X periodically", "what's on my cron", "when does X run", or any mention of `cron.json`. Read it before touching `cron.json` — the file has a strict schema, restart semantics, and a best-effort execution model that you must not misrepresent to the user.
4
+ ---
5
+
6
+ # typeclaw-cron
7
+
8
+ You have a cron file at `./cron.json` in your agent folder. It defines periodic jobs that the typeclaw runtime fires on schedule. This skill exists so you do not corrupt the file, do not promise behavior the runtime cannot deliver, and do not surprise the user.
9
+
10
+ ## What cron actually does
11
+
12
+ The typeclaw runtime starts a scheduler when the container boots. The scheduler reads `cron.json` **once** at startup. While the container runs, it fires each enabled job at its next scheduled time. There is no daemon outside the container — if the container is down, nothing runs.
13
+
14
+ This is a **best-effort scheduler**. Concretely:
15
+
16
+ - **Missed ticks are not replayed.** If the container was down at 23:30 and starts at 23:45, the 23:30 fire is lost forever.
17
+ - **Overlapping fires are skipped, not queued.** If a job is still running when its next tick arrives, the new tick is dropped (logged) and the next attempt is the tick after that.
18
+ - **There are no retries, no timeouts, no failure hooks.** A job that throws is logged and forgotten until its next scheduled fire.
19
+
20
+ Tell the user this if they ask about reliability. Do not invent guarantees the runtime does not give them.
21
+
22
+ ## The two job kinds
23
+
24
+ `cron.json` has one top-level key: `jobs`. Each job has a `kind` discriminator, plus shared fields and kind-specific fields.
25
+
26
+ ### Shared fields (all jobs)
27
+
28
+ | Field | Required | Notes |
29
+ | ---------- | -------- | ------------------------------------------------------------------------------------------------------------- |
30
+ | `id` | yes | Unique. Letters, digits, hyphens, underscores. Used in logs and to coalesce. |
31
+ | `schedule` | yes | Standard 5-field cron expression (`min hr dom mon dow`) or 6-field with seconds. See "Schedule syntax" below. |
32
+ | `enabled` | no | Defaults to `true`. Set to `false` to keep a job in the file but skip it. |
33
+ | `timezone` | no | IANA name like `Asia/Seoul`. Defaults to UTC (the container's timezone). |
34
+
35
+ ### `kind: "prompt"` — fire a prompt into a fresh session
36
+
37
+ ```json
38
+ {
39
+ "id": "daily-summary",
40
+ "schedule": "30 23 * * *",
41
+ "kind": "prompt",
42
+ "prompt": "Read today's session jsonl files in sessions/ and summarize the day into memory/."
43
+ }
44
+ ```
45
+
46
+ When this fires, the runtime opens a **brand new** `AgentSession` (yours, with your IDENTITY/SOUL/AGENTS files loaded), sends it the `prompt` text as if the user typed it, and disposes the session when done.
47
+
48
+ What this means for how you write prompts:
49
+
50
+ - **Treat the prompt as a self-contained instruction to your future self.** It runs in a session with no memory of past prompt-job runs unless you persist across runs (e.g. by writing to `MEMORY.md` or `memory/`).
51
+ - **The session has all your tools.** You can `read`, `write`, `bash`, edit files, commit to git — anything you can do in a normal turn.
52
+ - **There is no human on the other end.** No one will answer clarifying questions. Make the prompt complete and unambiguous.
53
+ - **Be specific about side effects.** "Summarize today" is vague. "Read every `sessions/*.jsonl` modified today and append a summary to `memory/$(date +%F)-summary.md`" is actionable.
54
+
55
+ ### `kind: "exec"` — run a shell command, no LLM
56
+
57
+ ```json
58
+ {
59
+ "id": "hourly-backup",
60
+ "schedule": "0 * * * *",
61
+ "kind": "exec",
62
+ "command": ["git", "commit", "-am", "hourly snapshot"]
63
+ }
64
+ ```
65
+
66
+ The runtime spawns the command directly with `Bun.spawn` from the agent folder (`/agent` inside the container). No agent session is created. No LLM call happens. The command's exit code and stderr are captured to container logs.
67
+
68
+ Use `exec` only for jobs that are pure mechanics — no judgement required. Examples that fit: git snapshots, log rotation, calling a script that already exists. Examples that **don't** fit: anything where "what do I commit" or "what should I write" depends on context. Use `prompt` for those.
69
+
70
+ `command` is an array. Index 0 is the executable, the rest are argv. Do **not** put a single shell pipeline in `command[0]` — that won't be parsed by a shell. If you need shell features (`|`, `>`, `&&`), wrap explicitly: `["sh", "-c", "your | pipeline | here"]`.
71
+
72
+ ## Schedule syntax
73
+
74
+ Standard cron, parsed by [`cron-parser`](https://github.com/harrisiirak/cron-parser). 5-field is the common form.
75
+
76
+ ```
77
+ * * * * *
78
+ ┬ ┬ ┬ ┬ ┬
79
+ │ │ │ │ └─ day of week (0-7, SUN-SAT, 0 and 7 both = Sunday)
80
+ │ │ │ └────── month (1-12, JAN-DEC)
81
+ │ │ └─────────── day of month (1-31)
82
+ │ └───────────────── hour (0-23)
83
+ └─────────────────────── minute (0-59)
84
+ ```
85
+
86
+ Common patterns:
87
+
88
+ | Schedule | Meaning |
89
+ | -------------- | ---------------------------------- |
90
+ | `*/15 * * * *` | every 15 minutes |
91
+ | `0 * * * *` | every hour, on the hour |
92
+ | `30 9 * * 1-5` | 09:30 every weekday |
93
+ | `0 0 * * 0` | Sunday midnight |
94
+ | `0 0 1 * *` | first day of every month, midnight |
95
+
96
+ Predefined aliases also work: `@hourly`, `@daily`, `@weekly`, `@monthly`, `@yearly`.
97
+
98
+ If you set `timezone`, the schedule is interpreted in that zone. **Always set `timezone` for any schedule that references wall-clock hours** (e.g. "every morning at 7"); otherwise it runs in UTC and will surprise the user.
99
+
100
+ ## Editing `cron.json` safely
101
+
102
+ `cron.json` is a single canonical file at the agent folder root. It is committed to git (not gitignored). Treat it like a config file you own.
103
+
104
+ ### Workflow
105
+
106
+ 1. **Read the whole file first** with the `read` tool. Don't assume what's in it.
107
+ 2. **Modify in memory.** Add, remove, or change jobs in the parsed JSON.
108
+ 3. **Write the whole file back** with the `write` tool. Always pretty-printed (2-space indent), trailing newline, sorted-stable order.
109
+ 4. **Apply with the `reload` tool.** Call the `reload` tool — it re-reads `cron.json` and updates the live scheduler. The tool returns `[cron] ok: ...` with an added/removed/updated/unchanged summary on success, or `[cron] failed: ...` with the exact validation error on failure. **If reload fails, the live schedule is left unchanged** — fix `cron.json` based on the error message and call `reload` again.
110
+ 5. **Commit the change** _after_ a successful reload. See the `typeclaw-git` skill for the commit-message rule (decision context required). `cron.json` is not gitignored, so an uncommitted edit will pollute your next commit.
111
+
112
+ ### Required fields checklist (catch this before writing)
113
+
114
+ For every job you add:
115
+
116
+ - `id` is unique within the file
117
+ - `id` matches `[a-zA-Z0-9_-]+` (no spaces, no slashes, no dots)
118
+ - `schedule` parses as cron
119
+ - `kind` is exactly `"prompt"` or `"exec"`
120
+ - If `prompt`: `prompt` is non-empty
121
+ - If `exec`: `command` is a non-empty array of non-empty strings
122
+ - If a wall-clock schedule was requested: `timezone` is set
123
+
124
+ ### Applying changes — the `reload` tool
125
+
126
+ The scheduler does **not** auto-reload `cron.json` when you edit it. You must call the `reload` tool to apply changes. There is no file watcher by design — reload is explicit so you always know when the live schedule changed.
127
+
128
+ **Safety contract**: reload validates `cron.json` first. If validation fails (bad JSON, invalid cron expression, duplicate id, etc.), the live schedule is left running with the previous configuration and `reload` returns the failure reason. Reload cannot break the running agent.
129
+
130
+ **The user can also reload from the host** with `typeclaw reload`. You don't need to ask them to — call the tool yourself when you finish an edit. But be aware they have the same primitive available.
131
+
132
+ If you finished an edit and the user only sees an in-flight job from the previous schedule, that job will complete naturally — reload never interrupts a running fire. Tell the user this if they wonder why their old job is still wrapping up.
133
+
134
+ ## Things you must not do
135
+
136
+ - **Do not edit `cron.json` from inside an `exec` job's `command`.** Exec jobs run without an LLM and have no way to call the `reload` tool, so the file mutation will not take effect until something else triggers a reload. If you genuinely need scheduled cron-management, write a `prompt` job whose prompt is "edit cron.json to ..." and let the prompt-fire's session call `reload` itself.
137
+ - **Do not put secrets in `prompt` or `command`.** `cron.json` is committed to git. Reference env vars or files instead (`["sh", "-c", "curl -H \"Authorization: Bearer $TOKEN\" ..."]`).
138
+ - **Do not promise sub-second precision or guaranteed execution.** This is best-effort — see "What cron actually does" above.
139
+ - **Do not invent fields the schema doesn't support** (no `retry`, `timeout`, `onFailure`, `concurrency`, etc.). They will be silently ignored at best, or rejected at worst.
140
+
141
+ ## When the user says "every X"
142
+
143
+ Pick `kind` first, then schedule, then timezone:
144
+
145
+ 1. **Does the work need judgement?** → `prompt`. Otherwise → `exec`.
146
+ 2. **Translate the cadence to cron.** "Every morning at 7" → `0 7 * * *`. "Every weekday at 9:30" → `30 9 * * 1-5`. "Every five minutes" → `*/5 * * * *`. If you are not sure, ask once. Don't guess on tricky cases like "every other Friday".
147
+ 3. **Timezone.** If the user mentioned a wall-clock time, set `timezone` to their zone. If unknown, ask once or default to the timezone in `USER.md` if it's recorded there.
148
+ 4. **Pick a stable `id`.** Use kebab-case that describes the job, not the schedule. `daily-summary` not `0-23-30`.
149
+ 5. **Write it. Call `reload`. If reload succeeded, commit it.** If reload failed, fix `cron.json` based on the error and retry — do not commit a broken file.
150
+
151
+ ## Reading cron history
152
+
153
+ There is no "list past fires" tool. To see what cron has done:
154
+
155
+ - **Container logs:** `docker logs <container>` shows `[cron] firing prompt <id>` and `[cron] <id> failed: ...` lines, plus stdout/stderr from `exec` jobs. The user runs this on the host stage.
156
+ - **Session jsonl:** every `prompt` fire creates a session under `sessions/`. The session metadata includes timestamps. You can `read` and `grep` these.
157
+ - **Git log:** if a job commits its work (e.g. the `daily-summary` example writes to `memory/`), `git log -- memory/` shows when it last ran.
158
+
159
+ If the user asks "did the daily summary run?", check the latest file in `memory/` and the most recent matching session under `sessions/`. Don't claim it ran if you can't see evidence.
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: typeclaw-git
3
+ description: Use this skill whenever you edit any file in the agent folder — config, cron, memory, identity, scaffolded markdown, scripts, anything tracked by git. Triggers include any `write`/`edit` tool call against a non-gitignored file, any time you finish a logical change, or any mention of "commit", "git", "version control", "history", or "what did I change". Read it before you edit, not after — the rule is *commit immediately after every edit, with decision context in the message*. Skipping the commit pollutes the next one; skipping the decision context makes the history useless to future-you.
4
+ ---
5
+
6
+ # typeclaw-git
7
+
8
+ Your agent folder is a git repo. Almost every file in it (`typeclaw.json`, `cron.json`, `MEMORY.md`, `IDENTITY.md`, `SOUL.md`, `USER.md`, `AGENTS.md`, scaffolded markdown, scripts you write, etc.) is tracked.
9
+
10
+ The contents of `.gitignore` split into two distinct categories — the distinction matters for this skill:
11
+
12
+ - **Truly ignored** (`.env`, `node_modules/`, `workspace/`, `mounts/`, `Dockerfile`, `.DS_Store`) — never in history, ever. Secrets, runtime junk, your free-write zone, and regenerated-on-start system files.
13
+ - **System-managed** (`sessions/`, `memory/`, `channels/`) — gitignored so _you_ don't stage them, but TypeClaw force-commits them on its own schedule. `sessions/` is auto-backed up by the runtime; `memory/` is committed by the dreaming subagent; `channels/` is runtime-owned channel state. Treat them as runtime-owned: do not `git add` them, do not write commit messages about them, and do not be alarmed when they appear in `git log`.
14
+
15
+ Everything not in either bucket is yours to commit.
16
+
17
+ This skill exists so the history of your agent folder stays a useful record of _why things changed_, not just a pile of "update X" commits.
18
+
19
+ ## The rule
20
+
21
+ **Every time you edit a tracked file, commit it before you move on. Every commit message records why the change was made, not just what changed.**
22
+
23
+ Two halves, both mandatory:
24
+
25
+ 1. **Commit immediately after the edit.** Don't batch unrelated changes. Don't leave dirty files lying around for the next turn to sweep up. The diff already shows what — the commit pins it to a moment with a reason.
26
+ 2. **Decision context in the message.** The diff answers _what_. The message must answer _why_. If commenting out the message body wouldn't change how a reader understands the commit, the message is failing its job.
27
+
28
+ This applies to every tracked file, not just `typeclaw.json` and `cron.json`. New file types we introduce later (memory snapshots, scaffolded skills, scripts) inherit the same rule by default.
29
+
30
+ ## What "decision context" means
31
+
32
+ A commit message body must answer at least:
33
+
34
+ - **Trigger** — what request, observation, or failure prompted this edit. Quote the user verbatim if they asked for it. Reference the prior state if you reacted to a problem.
35
+ - **Choice** — why this specific value/approach, especially when alternatives existed. Skip only when the choice is mechanical and obvious from the diff.
36
+ - **Constraints honored** — any skill rule, schema limit, or runtime semantic that shaped the edit, when it's relevant to understanding the choice.
37
+
38
+ A one-line subject is fine when the choice is mechanical (typo fix, formatting). The body is required whenever there was a real decision.
39
+
40
+ ### Good
41
+
42
+ ```
43
+ Add daily-summary cron at 23:30 KST
44
+
45
+ User asked for an end-of-day session recap. Picked `prompt` over
46
+ `exec` because summarization needs LLM judgement. Set
47
+ `timezone: Asia/Seoul` so the day boundary matches the user's
48
+ local clock, not UTC.
49
+ ```
50
+
51
+ ### Bad
52
+
53
+ ```
54
+ update cron
55
+ ```
56
+
57
+ ```
58
+ Add job
59
+ ```
60
+
61
+ ```
62
+ Change port to 8974
63
+ ```
64
+
65
+ (All three: no trigger, no choice, no constraints. Comment them out and nothing is lost.)
66
+
67
+ ## Workflow
68
+
69
+ 1. Edit the file with `write`/`edit`.
70
+ 2. Run any post-edit validation the file's domain requires (e.g. `reload` for `cron.json`, `jq .` sanity check for `typeclaw.json`). Don't commit broken files.
71
+ 3. `git add <file> && git commit -m "<subject>" -m "<body>"`. Imperative subject, body explains why.
72
+ 4. Move on.
73
+
74
+ If a single logical change touches multiple files (e.g. updating `typeclaw.json` _and_ a related script), commit them together — one commit, one decision. Don't split a single decision across commits.
75
+
76
+ If you discover an unrelated dirty file from a previous turn, commit it separately first with its own decision context (reconstruct from the diff and prior session if needed) before starting your current edit. Never let an unrelated change ride along.
77
+
78
+ ## Things you must not do
79
+
80
+ - **Do not skip the commit** "because the change is small." Small changes are exactly the ones that get lost. Toggling `enabled: false` on a cron job is a decision; commit it.
81
+ - **Do not write empty or generic messages** ("update", "fix", "change config"). The history exists to be read.
82
+ - **Do not amend or force-push** to clean up later. Sloppy history with real commits beats clean history that lies about when decisions happened.
83
+ - **Do not commit `.env` or anything truly-ignored.** If `git status` shows a truly-ignored file as staged, something is wrong with `.gitignore` — fix that first, don't commit the secret.
84
+ - **Do not commit `sessions/` or `memory/` either, even though `git log` shows them.** They're system-managed: TypeClaw's auto-backup and dreaming subagent own those commits. If you find one of them staged in your working tree, unstage it (`git restore --staged sessions/ memory/`) — your edit got mixed up with the runtime's domain.
85
+ - **Do not bundle unrelated changes.** One commit, one decision.
86
+
87
+ ## When you are unsure
88
+
89
+ If you can't articulate _why_ you're making a change in one sentence, you don't understand the change well enough to make it. Stop, re-read the request, and either ask the user or work it out before editing. Editing first and inventing a reason for the commit message later is the failure mode this skill exists to prevent.
@@ -0,0 +1,174 @@
1
+ ---
2
+ name: typeclaw-memory
3
+ description: Use this skill whenever the user asks what you remember, what you forgot, what you dreamed, why a fact is or isn't in your memory, when memory consolidation happens, or whenever you are about to read or write `MEMORY.md`, anything under `memory/`, or `memory/skills/`. Triggers include "what do you remember", "do you remember X", "forget that", "what did you dream", "when do you dream next", "why did you forget X", "edit MEMORY.md", "add to memory", "your daily streams", "memory-logger", "dreaming", "muscle memory", or any mention of `memory.idleMs` / `memory.dreaming.schedule` in `typeclaw.json`. Read it before you touch any memory file — `MEMORY.md` and `memory/yyyy-MM-dd.md` are runtime-owned, hand-edits are easy to do wrong, and the user almost always means something more specific than "edit memory" when they say it.
4
+ ---
5
+
6
+ # typeclaw-memory
7
+
8
+ You have a two-stage memory system, owned by the bundled `memory` plugin (auto-loaded on every TypeClaw agent — there is no `plugins[]` entry to add and no opt-out). Daily observations flow into `memory/yyyy-MM-dd.md` while you are awake; offline reflection consolidates them into `MEMORY.md` and may distill repeated procedures into muscle-memory skills under `memory/skills/`. Both stages are run by subagents the runtime spawns on its own — not tools you call directly.
9
+
10
+ This skill exists so you can answer the user's questions about your own memory honestly and so you do not corrupt it by hand-editing.
11
+
12
+ ## The two stages
13
+
14
+ ### Stage 1: memory-logger (online, per-session)
15
+
16
+ After every prompt completes, the runtime fires the `session.idle` hook. The memory plugin starts a debounce timer (`memory.idleMs`, default `10_000` ms; minimum `1000`). Every subsequent prompt completion resets the timer. When the user has been quiet for `idleMs`, the plugin spawns the **memory-logger** subagent for the current session. It also fires immediately on `session.end` (websocket close) so the final transcript never gets lost.
17
+
18
+ The memory-logger reads:
19
+
20
+ 1. `MEMORY.md` (long-term memory)
21
+ 2. The current `memory/yyyy-MM-dd.md` daily stream
22
+ 3. The transcript of the parent session past a watermark (the `entry=` value of the last fragment or watermark marker for that session)
23
+
24
+ It writes zero or more **fragments** to today's stream, plus a watermark marker so the next run knows where to resume. It writes nothing else, and it cannot run shell commands or edit existing content (its only tools are `read` and a custom `append`-only file tool — append never truncates, and a leading `\n` is auto-inserted if the existing file did not end in one).
25
+
26
+ A fragment looks like this in the daily stream:
27
+
28
+ ```
29
+ <!-- fragment source=<sessionId> entry=<entryId> -->
30
+ ## <topic>
31
+
32
+ **Claim:** <one-sentence assertion>
33
+ **Evidence:** <verbatim quote, named premise, or enumerated occurrences>
34
+ **Implication:** <how a future agent should behave differently because of this>
35
+ ```
36
+
37
+ The Claim/Evidence/Implication structure is **required** and the bar is intentionally high: no Implication, no fragment. The memory-logger explicitly disallows promoting session-bound style/tone to a stable preference, speculation about the user's emotions or motives, and any claim it cannot justify with evidence already in the transcript or existing memory.
38
+
39
+ ### Stage 2: dreaming (offline, scheduled)
40
+
41
+ The dreaming subagent runs on cron, configured under `memory.dreaming.schedule` (default `"*/30 * * * *"` — every 30 minutes). Multiple runs per day are the norm, not the exception; a fire with nothing past the watermark short-circuits before any LLM call, so most fires cost only a filesystem scan. The cron job id is `__plugin_memory_dreaming` (you cannot list it via the user-facing cron tools — it is plugin-owned).
42
+
43
+ When dreaming fires, it reads:
44
+
45
+ 1. `MEMORY.md`
46
+ 2. The **undreamed tail** of every `memory/yyyy-MM-dd.md` (the runtime tells it the exact line range — earlier lines are already consolidated into `MEMORY.md` and must NOT be re-read)
47
+
48
+ It rewrites `MEMORY.md` with the merged result, advances the per-day watermark in `memory/.dreaming-state.json`, optionally writes muscle-memory skills under `memory/skills/<name>/SKILL.md`, then `git commit -m "Dream"` the snapshot. After the commit, the runtime sets the `skip-worktree` index flag on the tracked memory artifacts so the user's `git status` and `git diff` stay clean. The flag is cleared and re-applied around every commit.
49
+
50
+ The dreaming subagent has only three tools: `read`, `write`, `ls`. No `bash`. No `edit`. It cannot run shell commands.
51
+
52
+ `MEMORY.md` after dreaming looks like:
53
+
54
+ ```
55
+ # Memory
56
+
57
+ ## <topic>
58
+ <conclusion paragraph in dreaming's own words>
59
+
60
+ fragments:
61
+ - memory/yyyy-MM-dd:<line>-<line>
62
+ - memory/yyyy-MM-dd:<line>-<line>
63
+
64
+ ## <topic>
65
+ <conclusion paragraph>
66
+
67
+ fragments:
68
+ - memory/yyyy-MM-dd:<line>-<line>
69
+ ```
70
+
71
+ The first line is always `# Memory`. Topics are level-2 headings. Every topic cites the source fragments by `memory/yyyy-MM-dd:<line>-<line>` so any claim is traceable back to the daily stream entry that justified it.
72
+
73
+ If the undreamed tails contain only watermarks, or every new fragment is already represented in `MEMORY.md`, dreaming **does nothing** and exits without writing. The watermark advances either way. "No-op dreaming" is a normal outcome, not a failure.
74
+
75
+ ### What gets injected into your prompt every turn
76
+
77
+ The plugin's `session.prompt` hook appends a `# Memory` section to your system prompt with:
78
+
79
+ - `MEMORY.md` (truncated to 12 KB; if larger, the rest is dropped with a `[truncated]` marker)
80
+ - The **undreamed tails** of each `memory/yyyy-MM-dd.md`, with bare watermark lines stripped (they are bookkeeping for the memory-logger, no signal for you)
81
+
82
+ Already-consolidated content is not injected twice — once a day's stream is fully dreamed, the loader drops it from the prompt entirely.
83
+
84
+ If `MEMORY.md` is missing, the section shows `[MISSING] Expected at: <path>`. If it exists but is empty (e.g. before the first dreaming run), it shows `[EMPTY] Present at <path> but has no content yet.`
85
+
86
+ ## What you must not do
87
+
88
+ - **Do not edit `MEMORY.md` directly.** It is dreaming-owned. The default system prompt says this verbatim. If you write to `MEMORY.md` from a normal session, your edit will survive only until the next dreaming run, which rewrites the file from scratch using the consolidation logic above. The user's intent is almost never "diff-edit `MEMORY.md`" — see "When the user asks ..." below for the right routings.
89
+ - **Do not write to `memory/yyyy-MM-dd.md`.** Daily streams are memory-logger's territory. The runtime reads watermarks out of these files; a hand-edit in the wrong place silently corrupts the cursor. (`memory/` is gitignored at the agent level but force-committed by the dreaming snapshot — your hand-edit there will not look untracked, but it will still be a bug.)
90
+ - **Do not write to `memory/skills/<name>/SKILL.md`.** That is the _muscle memory_ layer, owned exclusively by the dreaming subagent. The `typeclaw-skills` skill says the same thing from the skills-system angle; this skill says it from the memory angle. If you want a hand-authored skill, put it in `.agents/skills/` instead.
91
+ - **Do not write to `memory/.dreaming-state.json`.** It is internal bookkeeping (per-day line counts already consolidated). On malformed input the plugin fails open with empty state, so a wrong edit causes one redundant re-consolidation, but it is still a sign you misunderstood the contract.
92
+ - **Do not promise the user that an `idleMs` or `dreaming.schedule` change took effect just because you edited `typeclaw.json`.** Both fields are **restart-required** — the plugin reads them once at boot, and `reload` does not re-run plugin factories. Tell the user to run `typeclaw restart` (host stage).
93
+ - **Do not invent fragments.** If you find yourself wanting to "seed" a memory by hand, that is a symptom of the previous rules — surface the fact in your reply (so the memory-logger captures it) instead of writing to memory yourself.
94
+ - **Do not echo `[truncated]` or `[MISSING]` markers back at the user as if they were part of remembered content.** They are runtime annotations.
95
+
96
+ ## When the user asks "what do you remember?"
97
+
98
+ 1. Read `MEMORY.md`. Summarize at the topic level — do not dump the whole file unless asked. Cite specific topics by their level-2 headings.
99
+ 2. If relevant to the current task, also read the undreamed-tail of recent `memory/yyyy-MM-dd.md` files for fresh observations not yet consolidated. (Note: these are already in your prompt under `# Memory`, so usually you can just refer to them rather than re-reading.)
100
+ 3. If `MEMORY.md` is `[MISSING]` or `[EMPTY]`, say so plainly. The first dreaming run creates the file; if dreaming has never fired (e.g. no `memory.dreaming.schedule` configured, or fewer than ~24 hours since hatching), there is genuinely nothing yet.
101
+
102
+ ## When the user asks "do you remember X?"
103
+
104
+ 1. Search `MEMORY.md` and recent daily streams for a fragment matching X.
105
+ 2. If you find one: say what you found and cite the source (the topic heading from `MEMORY.md`, or the fragment line range from the daily stream).
106
+ 3. If you do not find one: say so plainly. **Do not invent a memory** to be helpful. The honest answer is "no, that is not in my memory" — the user can then decide whether to repeat the context now (which the memory-logger will pick up) or skip it.
107
+
108
+ ## When the user asks "forget X" / "remove X from your memory"
109
+
110
+ You cannot remove a fragment cleanly. The right response depends on what X is:
111
+
112
+ - **A fact in `MEMORY.md` that the user wants overridden** — surface a contradiction in your next reply ("noted: [X] is no longer correct, [Y] is what holds now"). The memory-logger picks the contradiction up as a fragment with the standard "supersedes existing memory" structure, and dreaming will replace the prior topic on its next run. The change is not instant — it lands at the next dreaming consolidation.
113
+ - **A specific fragment in a daily stream the user wants gone before it gets consolidated** — read the file, locate the fragment, propose the surgical edit to the user, and (only if they confirm) `write` the edited file back. **Do not delete the watermark line on the same fragment** — that breaks the memory-logger's cursor for the originating session.
114
+ - **Everything (full memory wipe)** — that is the user's call, not yours. Tell them: removing `MEMORY.md` is a one-line `rm`, but they should also remove `memory/.dreaming-state.json` so dreaming re-consolidates the still-present daily streams from scratch on its next run. If they want the daily streams gone too, `rm -rf memory/` (and the runtime will recreate the directory on the next memory-logger spawn). Confirm explicitly before any of this. Then commit the deletions with a `typeclaw-git`-compliant message naming what was removed and why.
115
+
116
+ ## When the user asks "what did you dream?" / "when do you dream next?"
117
+
118
+ 1. **What you dreamed**: read the most recent `Dream` git commit on your agent folder (`git log --grep='^Dream' -1`) and show the diff against `MEMORY.md` if useful. The commit timestamp tells you when dreaming last ran. If the answer is "no `Dream` commits yet", say that — `MEMORY.md` may exist but be the auto-created empty file from the first dreaming attempt.
119
+ 2. **When you dream next**: read `memory.dreaming.schedule` from `typeclaw.json` (default `"*/30 * * * *"` — every 30 minutes). Translate the cron expression to a wall-clock time in the agent's `TZ`. The dreaming cron job is **always registered** even when `memory.dreaming` is omitted; the default schedule applies. Tell the user honestly when the next fire is in the agent's local time.
120
+
121
+ ## When the user asks "what's a daily stream?" / "where is your memory stored?"
122
+
123
+ Stay concrete. Use this map:
124
+
125
+ | File / dir | What it is | Who writes it | Tracked in git |
126
+ | ------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------- |
127
+ | `MEMORY.md` | Long-term memory, consolidated topics with fragment citations. | Dreaming subagent (rewrites in full on each run). | Yes (force-committed under `Dream` commits, skip-worktree). |
128
+ | `memory/yyyy-MM-dd.md` | Daily fragment streams. Append-only during the day. | Memory-logger subagent (one fragment ≈ one prompt completion). | Gitignored, but force-committed in the dreaming snapshot. |
129
+ | `memory/skills/<name>/SKILL.md` | Muscle-memory skills distilled from recurring procedures. | Dreaming subagent only. | Gitignored, force-committed in the dreaming snapshot. |
130
+ | `memory/.dreaming-state.json` | Per-day watermarks (line counts already consolidated). Plain JSON, fail-open. | Dreaming subagent. | Gitignored, force-committed in the dreaming snapshot. |
131
+
132
+ `typeclaw init` does **not** scaffold any of these. They appear when needed — `MEMORY.md` and `memory/` are created by the first dreaming run; daily streams appear when the first memory-logger fires.
133
+
134
+ ## When the user asks about `memory.idleMs` or `memory.dreaming.schedule`
135
+
136
+ These are the only two configurable knobs. They live in the `memory` block of `typeclaw.json`:
137
+
138
+ ```json
139
+ {
140
+ "memory": {
141
+ "idleMs": 10000,
142
+ "dreaming": { "schedule": "*/30 * * * *" }
143
+ }
144
+ }
145
+ ```
146
+
147
+ | Field | Default | Effect | Reload class |
148
+ | -------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- |
149
+ | `memory.idleMs` | `10000` (min `1000`) | Debounce window before `memory-logger` spawns after a prompt completes. | Restart-required. |
150
+ | `memory.dreaming` | `{}` (cron job on) | Dreaming cron job is always registered. Override `schedule` to change when it fires. | Restart-required. |
151
+ | `memory.dreaming.schedule` | `"*/30 * * * *"` | Cron expression. Parsed via `cron-parser`; an invalid expression fails config load. Fires with nothing past the watermark short-circuit before any LLM call, so frequent no-op fires are intentionally cheap. | Restart-required. |
152
+
153
+ Both fields are restart-required because plugin config is read once at boot. After editing them, tell the user: "Edited `memory.<field>` — restart-required. Run `typeclaw restart` (host stage) to pick up the change." The bundled plugin's config schema is merged into `typeclaw.schema.json`, so editor autocomplete will validate these fields, but a `reload` will not re-instantiate the plugin.
154
+
155
+ To **disable dreaming entirely**, omit the `memory.dreaming` block. The cron job will not be registered. `MEMORY.md` will then never get consolidated automatically — the daily streams keep growing, and your prompt's `# Memory` section keeps showing more and more undreamed tails until the user re-enables dreaming. Warn them about this if they ask to disable it.
156
+
157
+ To **shorten the memory-logger debounce** (e.g. for testing): drop `memory.idleMs` toward `1000`. Anything below `1000` is rejected by the config schema. Cost: more memory-logger spawns, more turn latency from the spawn handshake (the spawn is async but the LLM cost is real).
158
+
159
+ ## When you are unsure whether something belongs in memory
160
+
161
+ Use this hierarchy. The first one that fits wins:
162
+
163
+ 1. **Operational lesson the next agent should follow** ("when the user says ‘ship it’, run typecheck before committing") → it belongs in **`AGENTS.md`**, not memory. AGENTS.md is your operating manual; memory is for facts and observations, not procedure rules.
164
+ 2. **A fact about the user** (their name, their preferences, their context) that you learned from this conversation → mention it in your reply with confident phrasing. The memory-logger will capture it. **Do not edit `USER.md` mid-session as a substitute for memory** — `USER.md` is for hatching-time identity and durable, user-confirmed traits, not for in-flight observations.
165
+ 3. **A multi-step procedure the user has guided you through more than once** that should become a reusable skill → flag the recurrence in your reply ("looks like we keep going through the same N-step flow for X"). Dreaming watches for repetition across daily streams and will distill it into `memory/skills/<name>/SKILL.md` if the bar is met (multi-step, recurred across multiple fragments / days, trigger conditions clearly statable, steps generalizable). You should not author muscle-memory skills directly.
166
+ 4. **An ephemeral observation** that doesn't change behavior — let it pass. Memory-logger has a strict bar; padding it with noise hurts the next agent's signal.
167
+
168
+ ## What this skill does _not_ cover
169
+
170
+ - **The `bunx skills` CLI and the broader skill ecosystem** (system / user / muscle-memory layers, lockfile-based "downloaded vs hand-authored", `bunx skills add/remove/update` workflow) — see `typeclaw-skills`.
171
+ - **Editing `typeclaw.json` outside the `memory` block** (port, model, mounts, plugins, channels) — see `typeclaw-config`.
172
+ - **The cron file format and scheduling** (`cron.json`) — see `typeclaw-cron`. The dreaming cron job is plugin-owned and lives outside `cron.json`; you cannot configure or list it through the cron skill.
173
+ - **Plugin authoring** (`definePlugin`, contributing tools/subagents/cron jobs) — see `typeclaw-plugins`. The memory plugin is an example of the patterns that skill describes.
174
+ - **Identity files** (`IDENTITY.md`, `SOUL.md`, `USER.md`, `AGENTS.md`) — these are not memory. Edit them directly when relevant; no skill needed for that.
@@ -0,0 +1,175 @@
1
+ ---
2
+ name: typeclaw-monorepo
3
+ description: Use this skill whenever you are about to write code that another part of your agent folder might want to reuse, or you need to decide where new code goes. Triggers include "create a package", "extract this into a library", "make this reusable", "where should this script live", "add a workspace", "edit root scripts", "bun workspaces", "packages/", "monorepo", or any tool/utility/library you intend to call from more than one place. Read it before scaffolding anything under `packages/` — the layout has conventions for plugins, custom scripts, and root-level wiring that you must not improvise around.
4
+ ---
5
+
6
+ # typeclaw-monorepo
7
+
8
+ Your agent folder is a **bun monorepo**. The root `package.json` declares `"workspaces": ["packages/*"]`, and each subdirectory of `packages/` is a fully independent bun package that can be installed, depended on, and run from anywhere in the agent folder. This skill exists so you put new code in the right place, follow the workspace conventions, and do not surprise the user with random script files scattered around.
9
+
10
+ ## The two zones, and which to pick
11
+
12
+ You have two free-write zones at the agent root: `workspace/` and `packages/`. Both are exempt from the non-workspace-write guard so you can edit them without acknowledging anything, but their relationship to git is opposite, and picking the wrong one is the most common mistake.
13
+
14
+ | Zone | Purpose | Tracked in git? | Reusable? |
15
+ | ------------ | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
16
+ | `workspace/` | One-off scripts, scratch work, throwaway experiments | **No** — entire dir is gitignored | No (the dir itself is invisible to git) |
17
+ | `packages/` | Reusable packages, custom plugins, shared utilities, internal libs | **Yes** — every file is tracked and MUST be committed when edited (only `*/node_modules/` ignored inside) | Yes (committed and importable across agents) |
18
+
19
+ The two columns to internalize:
20
+
21
+ - **Guard allowlist** (write permission): `workspace/` and `packages/` are both free-write — no `acknowledgeGuards` needed. This is about whether the agent can write the file at all.
22
+ - **Git tracking** (persistence): `workspace/` is gitignored end-to-end (a true scratch zone, lost on clone), while `packages/` is fully tracked (committed to git, shipped with the agent folder, visible in PRs). This is about whether the file survives.
23
+
24
+ Anything you put in `packages/` MUST land in a commit — see `typeclaw-git`. The non-`node_modules/` files in `packages/` are not gitignored; treating them as throwaway will surprise the user when their PR includes a half-baked package they did not expect.
25
+
26
+ **Decision rule, top to bottom — stop at the first match:**
27
+
28
+ 1. **Will another script or another part of the agent folder import this?** → `packages/<name>/`. Even if "another part" is just "tomorrow's me writing a sibling script", a reusable thing belongs here.
29
+ 2. **Is this a custom typeclaw plugin** (anything you'd list in `typeclaw.json`'s `plugins`)? → `packages/<plugin-name>/`. Always. Plugins are the canonical packages.
30
+ 3. **Will the user want to track this in git, see it in PRs, depend on it from a cron job?** → `packages/<name>/`.
31
+ 4. **Is this throwaway** — a one-shot data transformation, a debug script, a scratch experiment that exists for one task and dies? → `workspace/`.
32
+ 5. **Default if unsure** → `packages/<name>/`. Better to commit something reusable than to lose something useful in the gitignored void.
33
+
34
+ The hidden cost of getting this wrong: code in `workspace/` is invisible to git (the entire `workspace/` dir is gitignored as a "free-write zone"), so a reusable utility you put there will silently disappear on the next clone, the next session that runs `git clean`, or the next time a user tarballs the agent folder for backup.
35
+
36
+ ## Anatomy of a `packages/<name>/` package
37
+
38
+ A bun workspace package is just a normal package directory:
39
+
40
+ ```
41
+ packages/
42
+ my-utility/
43
+ package.json # name, version, dependencies, scripts
44
+ index.ts # entrypoint
45
+ index.test.ts # tests, if appropriate
46
+ ```
47
+
48
+ Minimal `packages/my-utility/package.json`:
49
+
50
+ ```json
51
+ {
52
+ "name": "my-utility",
53
+ "version": "0.0.0",
54
+ "private": true,
55
+ "type": "module",
56
+ "main": "./index.ts",
57
+ "exports": {
58
+ ".": "./index.ts"
59
+ }
60
+ }
61
+ ```
62
+
63
+ Notes that catch people:
64
+
65
+ - **`private: true`** keeps the package out of any accidental publish. Default to `true` unless the user explicitly asks for a publishable package.
66
+ - **`type: "module"`** matches the agent root and avoids ESM/CJS surprises. Always set it.
67
+ - **`main` and `exports` point at `index.ts` directly.** Bun executes TypeScript natively in the workspace; no build step required.
68
+ - **`name` is the import specifier.** `import { foo } from 'my-utility'` works from anywhere in the agent folder once you `bun install`. Pick a name you would not be embarrassed to type — descriptive and lowercase, no scope unless you have a reason.
69
+ - **No version churn.** Workspace packages stay at `0.0.0` unless the user explicitly versions them. Bun resolves them by name, not by version.
70
+
71
+ ## Wiring a workspace package into another package
72
+
73
+ To depend on `packages/my-utility` from another package (e.g. `packages/my-plugin`), add it to that package's `dependencies` with the workspace protocol:
74
+
75
+ ```json
76
+ {
77
+ "name": "my-plugin",
78
+ "dependencies": {
79
+ "my-utility": "workspace:*"
80
+ }
81
+ }
82
+ ```
83
+
84
+ Then `bun install` from the agent folder root. Bun symlinks the workspace package into `packages/my-plugin/node_modules/my-utility` and `import` resolves it normally. The `*` matches any version because workspace packages don't track versions.
85
+
86
+ To depend on a workspace package from the **agent root** (e.g. so cron `exec` jobs or root scripts can call into it), add it to the **root** `package.json#dependencies` the same way:
87
+
88
+ ```json
89
+ {
90
+ "dependencies": {
91
+ "typeclaw": "file:../typeclaw",
92
+ "agent-browser": "^0.26.0",
93
+ "my-utility": "workspace:*"
94
+ }
95
+ }
96
+ ```
97
+
98
+ ## Custom typeclaw plugins live under `packages/`
99
+
100
+ If you are writing a typeclaw plugin (anything that uses `definePlugin` from `typeclaw/plugin`), the canonical home is `packages/<plugin-name>/`. The workflow:
101
+
102
+ 1. **Author**: `packages/my-plugin/index.ts` exports `definePlugin({ ... })` as default.
103
+ 2. **Wire**: edit `typeclaw.json` so the `plugins` array contains the local path:
104
+
105
+ ```json
106
+ {
107
+ "plugins": ["./packages/my-plugin"]
108
+ }
109
+ ```
110
+
111
+ 3. **Per-plugin config block** uses the **derived name** (basename of the path with extensions stripped — see the `typeclaw-plugins` skill for the full naming rule). For `./packages/my-plugin`, the derived name is `my-plugin`, so:
112
+
113
+ ```json
114
+ {
115
+ "plugins": ["./packages/my-plugin"],
116
+ "my-plugin": {
117
+ "/* ... your config ... */": ""
118
+ }
119
+ }
120
+ ```
121
+
122
+ 4. **Restart**: `plugins[]` is `restart-required`. Run `typeclaw restart` (or call the `restart` tool); reload alone will not pick up a new plugin entry.
123
+
124
+ Read the `typeclaw-plugins` skill before authoring the plugin code — it covers `definePlugin`, hooks, tools, subagents, cron, the engine boundary, and the failure-mode error messages.
125
+
126
+ ## Editing the root `package.json`
127
+
128
+ The root `package.json` is in the agent root's writable allowlist (alongside `AGENTS.md`, `IDENTITY.md`, etc.) and is fair game for additions. **Welcome editing patterns:**
129
+
130
+ - **`scripts`**: add convenience scripts the user (or you, in cron `exec` jobs) can run. Example: `"scripts": { "summarize": "bun run packages/summarizer/index.ts" }`. Then `bun summarize` from anywhere in the agent folder runs it.
131
+ - **`dependencies`** / **`devDependencies`**: add libraries you actually use. Run `bun install` after adding. Don't add deps speculatively — only what the code imports today.
132
+ - **`workspaces`**: by default `["packages/*"]`. The user may extend it (e.g. `["packages/*", "tools/*"]`) if they want a second workspace root. Don't change the existing entry without a reason.
133
+
134
+ **Patterns to avoid at the root:**
135
+
136
+ - **Don't add a `main` or `bin` field at the root.** The root package is a workspace coordinator, not an executable. Put entrypoints inside `packages/<name>/`.
137
+ - **Don't change `name`, `private`, or `type`.** These are scaffolded by typeclaw and the rest of the system assumes them.
138
+ - **Don't add the typeclaw or agent-browser deps to packages.** They live at the root and are shared via the bun workspace mechanism. If a workspace package needs typeclaw types, depend on it via `"typeclaw": "workspace:*"` only if it needs the plugin SDK at runtime — and even then, `import { definePlugin } from 'typeclaw/plugin'` resolves through the root's `node_modules` automatically.
139
+
140
+ ## When you start a new package, do this exactly
141
+
142
+ 1. **Pick a name.** Lowercase, kebab-case, descriptive. `journal-store`, not `js`. Plugin packages are named after what the plugin does, not what it is — `standup-log`, not `my-plugin`.
143
+ 2. **Create the directory.** `packages/<name>/`.
144
+ 3. **Write `package.json`** using the minimal template above. Set `name` to the directory name (they don't have to match, but matching avoids confusion).
145
+ 4. **Write `index.ts`** with the actual code.
146
+ 5. **Add tests** as `<file>.test.ts` next to the implementation if the logic is non-trivial.
147
+ 6. **`bun install` from the agent root** — only needed when you add dependencies. Bun automatically links new workspace packages on install; you don't need to manually register the package anywhere.
148
+ 7. **Commit.** Per `typeclaw-git`, commit the new package immediately with the decision context — why this is reusable, what it solves.
149
+
150
+ ## `bun install` and the monorepo
151
+
152
+ Run `bun install` **from the agent root**, never from inside a `packages/<name>/` directory. The root install:
153
+
154
+ - Resolves all workspace packages and creates symlinks
155
+ - Hoists shared dependencies to the root `node_modules/`
156
+ - Honors per-package dependencies that are not in the root
157
+
158
+ Per-package `node_modules/` is gitignored (`packages/*/node_modules/` in `.gitignore`), but those directories rarely have anything in them after a hoist — bun puts most things at the root.
159
+
160
+ If you see `Cannot find module 'my-utility'` after creating a new workspace package, the fix is `bun install` from the agent root. There is no separate "register the workspace" step.
161
+
162
+ ## Things you must not do
163
+
164
+ - **Do not put reusable code in `workspace/`.** It will be lost. Re-read the decision rule above if you're tempted.
165
+ - **Do not put one-off throwaway scripts in `packages/`.** Each `packages/<name>` is a real package the user will see in git, in PRs, and in dependency graphs. Keep it small. If it's a 10-line script that runs once, `workspace/scratch-<date>.ts` is correct.
166
+ - **Do not edit the root `workspaces` field to point outside `packages/*`** unless the user explicitly asks. The default convention is part of the agent's identity layout; extending it surprises every other tool that walks the workspace.
167
+ - **Do not version workspace packages with semver** unless the user is publishing them. Internal packages stay at `0.0.0` and depend on each other via `workspace:*`.
168
+ - **Do not nest workspaces.** A package inside `packages/` cannot itself declare `workspaces`. Bun rejects nested workspaces, and even if it didn't, the cognitive load is not worth it.
169
+ - **Do not add `node_modules/` to `packages/<name>/.gitignore`.** The root `.gitignore` already covers `packages/*/node_modules/`. Adding a sub-`.gitignore` is noise that will get out of sync.
170
+
171
+ ## Cross-references
172
+
173
+ - **`typeclaw-plugins`** — read this before authoring any custom plugin. Covers the plugin SDK, hooks, tools, subagents, cron, naming derivation, and failure modes.
174
+ - **`typeclaw-git`** — commit policy. Every new package and every meaningful edit to `package.json` (root or workspace) gets a commit immediately with decision context.
175
+ - **`typeclaw-cron`** — if your package is invoked from a cron job, the `prompt` vs `exec` choice and the schedule semantics live there.