diff --git a/.gitattributes b/.gitattributes index f865be5..59b903b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,5 @@ calcit.cirru -diff linguist-generated yarn.lock -diff linguist-generated +Agents.md -diff linguist-generated +llms/*.md -diff linguist-generated diff --git a/.npmignore b/.npmignore index 8a2ded2..481dde2 100644 --- a/.npmignore +++ b/.npmignore @@ -19,3 +19,5 @@ compact.cirru .compact-inc.cirru .calcit-error.cirru LICENSE + +llms/ diff --git a/Agents.md b/Agents.md new file mode 100644 index 0000000..3d975ff --- /dev/null +++ b/Agents.md @@ -0,0 +1,610 @@ +# Cumulo Development Guide for LLM Agents + +**🤖 This guide helps LLM agents add features to Cumulo applications based on [calcium-workflow](https://github.com/Cumulo/calcium-workflow) template.** + +📚 **Related**: [Respo UI](./llms/Respo.md) + +--- + +## Quick Reference + +**What is Cumulo?** Realtime fullstack framework using **diff/patch** for server-client sync. + +**Core Architecture** (already in template): + +- **Server**: `*reel` atom → `updater` (pure fn) → `twig-container` → `diff-twig` → patches +- **Client**: WebSocket → `patch-twig` → `*store` atom → Respo render +- **Key libs**: `recollect` (diff/patch), `cumulo-reel` (time-travel), `ws-edn` (WebSocket) + +**Project structure** (from template): + +``` +app.client # Client entry (already configured) +app.server # Server entry (already configured) +app.schema # ← ADD your data structures here +app.updater # ← ADD your business logic here +app.twig.* # ← ADD your view projections here +app.comp.* # ← ADD your UI components here +``` + +--- + +## Development Workflow - Adding Features + +--- + +## Development Workflow - Adding Features + +### Standard Process (5 Steps) + +**Example**: Add a "tasks" feature where users can create/manage tasks. + +#### 1. **Update Schema** (Define data structure) + +```bash +cr query def app.schema/user +cr tree append-child app.schema/user -p "1" -e ":tasks (noted task ({}))" +``` + +Add to `app.schema` namespace if needed: + +```cirru +def task + {} + :id nil + :title | + :done false + :created-at nil +``` + +#### 2. **Create Updater** (Business logic - pure function) + +```bash +cr edit def app.updater.task/add-task -e \ + 'defn add-task (db title sid op-id op-time) + let + user-id $ get-in db ([] :sessions sid :user-id) + task $ {} + :id op-id + :title title + :done false + :created-at op-time + assoc-in db ([] :users user-id :tasks op-id) task' +``` + +**Key pattern**: `(db ...) → new-db` (pure function, returns new state) + +#### 3. **Wire Updater** (Connect to main dispatcher) + +```bash +# Find insertion point +cr query def app.updater/updater +cr tree show app.updater/updater -p "2" + +# Add new branch +cr tree insert-before app.updater/updater -p "2,8" -e \ + '(:task/add title) (task/add-task db title sid op-id op-time)' +``` + +#### 4. **Update Twig** (Control what clients see) + +```bash +cr query def app.twig.container/twig-container +# Then add/modify twig logic +``` + +Example: Include tasks in user twig + +```cirru +defn twig-user (user) + -> user + dissoc :password + update :tasks $ fn (tasks) + ; Only send recent 20 tasks + -> tasks vals + sort-by :created-at + reverse + take 20 +``` + +#### 5. **Create/Update UI** (Respo component) + +See [Respo.md](./llms/Respo.md) for UI development. + +```cirru +defn comp-task-list (tasks) + div ({} (:class-name css/task-list)) + -> tasks vals + map $ fn (task) + [] (:id task) + comp-task-item task +``` + +--- + +## Key Patterns for Development + +### 1. **Updater Pattern** (Most Common Changes) + +**Template**: + +```cirru +defn your-updater (db data sid op-id op-time) + ; 1. Extract user context + let + user-id $ get-in db ([] :sessions sid :user-id) + ; 2. Validate if needed + if (valid? data) + ; 3. Return new db (pure!) + assoc-in db ([] :users user-id :new-field) data + ; 4. Or add error message + add-error-message db sid "|Validation failed" +``` + +**Constraints**: + +- ✅ Pure function (no I/O, no side effects) +- ✅ Always return `db` +- ✅ Use `get-in`, `assoc-in`, `update-in` for nested updates +- ❌ No `println`, `wss-send!`, file operations inside updater + +### 2. **Twig Pattern** (Control Data Visibility) + +**Template**: + +```cirru +defn twig-your-feature (db session) + let + user-id $ :user-id session + if (some? user-id) + ; Return only what this client should see + {} + :my-data $ get-in db ([] :users user-id :my-data) + :shared-data $ compute-shared-data db user-id + ; Not logged in - return minimal data + {} +``` + +**Constraints**: + +- ✅ Filter by `session` (never send all users' data) +- ✅ Remove sensitive fields (`:password`, etc.) +- ✅ Consider `defn-memoized` for expensive calculations +- ❌ Don't return entire `db` + +### 3. **Wiring Pattern** (Connect to Dispatcher) + +**Find location**: + +```bash +cr query def app.updater/updater +cr tree show app.updater/updater -p "2" # tag-match cases +``` + +**Add case** (use same structure as existing ones): + +```cirru +tag-match op + ; Existing cases... + (:your-op/action arg1 arg2) + your-updater db arg1 arg2 sid op-id op-time +``` + +--- + +## Common Modification Scenarios + +### A. Add Field to Existing Schema + +```bash +cr tree append-child app.schema/user -p "1" -e ":new-field default-value" +``` + +### B. Modify Updater Logic + +```bash +# 1. Find and read current code +cr query def app.updater.user/log-in + +# 2. Locate exact position +cr tree show app.updater.user/log-in -p "2" + +# 3. Replace specific node +cr tree replace app.updater.user/log-in -p "2,1,0" -e 'new-logic here' +``` + +### C. Add New Twig Projection + +```bash +cr edit def app.twig.user/twig-user-profile -e \ + 'defn twig-user-profile (user) + {} + :id $ :id user + :name $ :name user + :stats $ compute-user-stats user' +``` + +### D. Wire New Operation + +```bash +cr tree insert-after app.updater/updater -p "2,5" -e \ + '(:new/operation data) (new-updater db data sid op-id op-time)' +``` + +--- + +## Critical Constraints + +### ⚠️ **Never Do These in Updaters** + +```cirru +; ❌ BAD: Side effects +defn bad-updater (db sid) + println |Debug message ; Side effect! + wss-send! sid data ; Side effect! + check-write-file! ... ; I/O! + assoc-in db ... + +; ✅ GOOD: Pure function +defn good-updater (db sid) + assoc-in db ([] :sessions sid :data) new-value + +; ✅ Side effects in dispatch! (OK) +defn dispatch! (op sid) + println |Logging ; OK here + reset! *reel $ reel-reducer @*reel updater op sid op-id op-time +``` + +### ⚠️ **Never Expose Sensitive Data in Twigs** + +```cirru +; ❌ BAD: Leaks passwords +defn bad-twig (user) + user ; Contains :password! + +; ✅ GOOD: Filter sensitive data +defn good-twig (user) + dissoc user :password + +; ✅ BETTER: Explicit whitelist +defn safe-twig (user) + {} + :id $ :id user + :name $ :name user +``` + +### ⚠️ **Never Send All Data to All Clients** + +```cirru +; ❌ BAD: Privacy violation +defn bad-twig-container (db session) + {} + :all-users (:users db) ; Everyone sees everyone! + +; ✅ GOOD: Session-filtered +defn good-twig-container (db session) + if-let (user-id (:user-id session)) + {} + :current-user $ twig-user $ get-in db ([] :users user-id) + :public-data $ get-public-data db + {} +``` + +--- + +## Debugging Workflow + +### Check Current State + +```bash +# View schema +cr query def app.schema/database + +# Trace updater flow +cr query usages app.updater/updater + +# Find where something is defined +cr query find your-function-name + +# See all operations +cr query search app.updater/updater -p "tag-match" -l +``` + +### Test Changes + +```bash +# Syntax check only (fast) +cr --check-only + +# Run once and exit +cr -1 + +# Compile JS once +cr -1 js +``` + +### Server Logs (set `dev? true` in config) + +```cirru +; In dispatch! and sync-clients!, you'll see: +; "Dispatch!" :user/log-in {...} +; "Changes for" "sid-123" : [[:assoc [:field] value]] +``` + +--- + +## Performance Tips + +### When to Memoize Twigs + +```cirru +ns app.twig.analytics $ :require + memof.alias :refer $ defn-memoized + +; ✅ Memoize expensive calculations called in render loop +defn-memoized compute-user-stats (user-data timestamp) + ; Heavy computation... + ... +``` + +**Use when**: + +- Called in `twig-container` (runs every 100ms) +- Pure function with repeated inputs +- Expensive: sorting, filtering, aggregations + +**Don't use when**: + +- Function has side effects +- Arguments always change + +### Optimize Diffs + +```cirru +; Always provide :key for list of objects +diff-twig old-tasks new-tasks $ {} (:key :id) +``` + +--- + +## Common Patterns Reference + +### Session Management (rarely needs changes) + +```cirru +; Template already handles connect/disconnect +; You mainly add session-specific data: + +defn add-user-data-to-session (db sid data op-id op-time) + assoc-in db ([] :sessions sid :user-data) data +``` + +### Authentication (template has basic version) + +### Authentication (template has basic version) + +Extend with custom logic: + +```cirru +defn user/log-in (db username password sid op-id op-time) + let + maybe-user $ find-user db username + if + and (some? maybe-user) + = (md5 password) (:password maybe-user) + ; Success: Set user-id in session + assoc-in db ([] :sessions sid :user-id) (:id maybe-user) + ; Failure: Add error message + add-error-message db sid "|Invalid credentials" +``` + +### Router Integration (template configured) + +```cirru +; Client triggers route change +dispatch! $ :: :router/change ({} (:name :profile)) + +; Server stores in session (already in template) +; Twig returns page-specific data based on router +``` + +--- + +## Rapid Reference + +### CLI Commands for Development + +```bash +# Development cycle +cr --check-only # Fast syntax check +cr -1 # Run once +cr js # Watch compile +mode=dev cr --entry server # Dev server + +# Code exploration +cr query def app.updater/updater +cr query usages your-function +cr query find symbol-name + +# Code modification +cr tree show path/to/func -p "2,1" +cr tree replace path/to/func -p "2,1,0" -e 'new-code' +cr edit def new/function -e 'defn ...' +``` + +### Common Code Patterns + +```cirru +; Get user from session +let + user-id $ get-in db ([] :sessions sid :user-id) + user $ get-in db ([] :users user-id) + ... + +; Add nested field +assoc-in db ([] :users user-id :field) value + +; Update nested field +update-in db ([] :users user-id :field) inc + +; Check if logged in +if-let (user-id (get-in db ([] :sessions sid :user-id))) + ; Logged in + ... + ; Not logged in + ... + +; Filter map +-> data + filter $ fn (entry) + condition? entry + +; Transform list +-> items + map $ fn (item) + transform item + take 10 +``` + +--- + +## Error Patterns + +### Show Error to User + +```cirru +defn add-error-message (db sid message) + let + msg-id $ generate-id! + assoc-in db ([] :sessions sid :messages msg-id) + {} + :id msg-id + :text message + :kind :error +``` + +### Client Displays (template has `comp-messages`) + +```cirru +; Already in template's container component +comp-messages (:messages session) + {} $ :on-clear $ fn (msg-id) + dispatch! $ :: :session/remove-message msg-id +``` + +--- + +## Testing + +```bash +cr --check-only # Syntax only (fast) + +cr --check-only # Syntax only (fast) +cr -1 # Run full cycle once +``` + +--- + +## Common Pitfalls (Critical!) + +### ❌ **Side Effects in Updaters** + +```cirru +; ❌ WRONG +defn bad-updater (db sid) + println "|Logging in" ; NO! + wss-send! sid data ; NO! + assoc-in db ... + +; ✅ CORRECT +defn good-updater (db sid) + assoc-in db ... ; Pure function + +; ✅ Side effects belong in dispatch! +defn dispatch! (op sid) + println "|Debug" ; OK here + reset! *reel $ reel-reducer @*reel updater op sid op-id op-time +``` + +### ❌ **Exposing Sensitive Data** + +```cirru +; ❌ WRONG: Leaks password +defn bad-twig (user) + user + +; ✅ CORRECT: Filter +defn good-twig (user) + dissoc user :password +``` + +### ❌ **Broadcasting Private Data** + +```cirru +; ❌ WRONG: Everyone sees all users +defn bad-twig-container (db session) + {} $ :all-users (:users db) + +; ✅ CORRECT: Filter by session +defn good-twig-container (db session) + if-let (user-id (:user-id session)) + {} $ :user $ twig-user $ get-in db ([] :users user-id) + {} +``` + +### ❌ **Mutating State** + +```cirru +; ❌ WRONG +defn bad (db) + assoc! db :key val ; Mutates! + db + +; ✅ CORRECT +defn good (db) + assoc db :key val ; Returns new +``` + +--- + +## Summary: Development Checklist + +When adding a feature: + +1. ✅ **Schema** - Define data structure in `app.schema` +2. ✅ **Updater** - Pure function `(db ...) → new-db` in `app.updater.*` +3. ✅ **Wire** - Add case to `app.updater/updater` tag-match +4. ✅ **Twig** - Filter data by session in `app.twig.*` +5. ✅ **UI** - Create Respo component in `app.comp.*` +6. ✅ **Test** - `cr --check-only` before commit + +**Key Rules**: + +- Updaters must be **pure** (no I/O, no side effects) +- Twigs must **filter by session** (never leak private data) +- Always **dissoc :password** before sending to client +- Use **`(:: :namespace/action args)`** for operations +- Test with **`cr --check-only`** frequently + +**Template handles** (don't need to modify often): + +- WebSocket lifecycle (connect/disconnect) +- Render loop (100ms sync) +- Client patch application +- Basic session management +- Reel/time-travel setup + +**You mainly add**: + +- New schemas +- New updaters +- New twigs +- New UI components + +--- + +**Further Reading**: + +- Calcit tools → [Calcit.md](./llms/Calcit.md) +- UI framework → [Respo.md](./llms/Respo.md) +- Template source → https://github.com/Cumulo/calcium-workflow diff --git a/calcit.cirru b/calcit.cirru index 8f4e21e..e5403ec 100644 --- a/calcit.cirru +++ b/calcit.cirru @@ -1,9 +1,9 @@ {} (:package |app) - :configs $ {} (:init-fn |app.server/main!) (:port 6001) (:reload-fn |app.server/reload!) (:version |0.9.11) + :configs $ {} (:init-fn |app.server/main!) (:reload-fn |app.server/reload!) (:version |0.9.12) :modules $ [] |lilac/ |memof/ |recollect/ |cumulo-util.calcit/ |ws-edn.calcit/ |bisection-key/ |respo-markdown.calcit/ :entries $ {} - :client $ {} (:init-fn |app.client/main!) (:reload-fn |app.client/reload!) + :client $ {} (:init-fn |app.client/main!) (:reload-fn |app.client/reload!) (:version |0.0.0) :modules $ [] |lilac/ |memof/ |recollect/ |respo.calcit/ |respo-ui.calcit/ |respo-message.calcit/ |cumulo-util.calcit/ |ws-edn.calcit/ |respo-feather.calcit/ |alerts.calcit/ |respo-markdown.calcit/ |bisection-key/ |gen-code/ :files $ {} |app.bookmark $ %{} :FileEntry @@ -42634,526 +42634,530 @@ |app.util.compile $ %{} :FileEntry :defs $ {} |handle-compact-files! $ %{} :CodeEntry (:doc |) - :code $ %{} :Expr (:at 1596297781796) (:by |S1lNv50FW) + :code $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596297781796) (:by |S1lNv50FW) (:text |defn) - |j $ %{} :Leaf (:at 1596297781796) (:by |S1lNv50FW) (:text |handle-compact-files!) - |r $ %{} :Expr (:at 1596297781796) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |defn) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |handle-compact-files!) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1596343141010) (:by |S1lNv50FW) (:text |pkg) - |L $ %{} :Leaf (:at 1596343118541) (:by |S1lNv50FW) (:text |old-files) - |P $ %{} :Leaf (:at 1624244134032) (:by |S1lNv50FW) (:text |latest-files) - |j $ %{} :Leaf (:at 1596297781796) (:by |S1lNv50FW) (:text |added-names) - |r $ %{} :Leaf (:at 1596297781796) (:by |S1lNv50FW) (:text |removed-names) - |v $ %{} :Leaf (:at 1596297781796) (:by |S1lNv50FW) (:text |changed-names) - |x $ %{} :Leaf (:at 1599733142405) (:by |S1lNv50FW) (:text |configs) - |xT $ %{} :Leaf (:at 1636635188766) (:by |S1lNv50FW) (:text |entries) - |y $ %{} :Leaf (:at 1624244173977) (:by |S1lNv50FW) (:text |filter-ns) - |v $ %{} :Expr (:at 1596297786437) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |pkg) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |old-files) + |X $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |latest-files) + |Z $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |added-names) + |b $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |removed-names) + |d $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |changed-names) + |f $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |configs) + |h $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |entries) + |j $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |filter-ns) + |Z $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596297787851) (:by |S1lNv50FW) (:text |let) - |j $ %{} :Expr (:at 1596297788534) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |let) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Expr (:at 1624244126867) (:by |S1lNv50FW) + |T $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1624244129097) (:by |S1lNv50FW) (:text |new-files) - |r $ %{} :Expr (:at 1624244160589) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |new-files) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1624244160589) (:by |S1lNv50FW) (:text |if) - |j $ %{} :Expr (:at 1624244160589) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |if) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1624244160589) (:by |S1lNv50FW) (:text |some?) - |j $ %{} :Leaf (:at 1624244178635) (:by |S1lNv50FW) (:text |filter-ns) - |r $ %{} :Expr (:at 1624244160589) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |some?) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |filter-ns) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1624244160589) (:by |S1lNv50FW) (:text |let) - |j $ %{} :Expr (:at 1624244160589) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |let) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Expr (:at 1624244160589) (:by |S1lNv50FW) + |T $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1624244160589) (:by |S1lNv50FW) (:text |target) - |j $ %{} :Expr (:at 1624244160589) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |target) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1624244191062) (:by |S1lNv50FW) (:text |get) - |b $ %{} :Leaf (:at 1624244193097) (:by |S1lNv50FW) (:text |latest-files) - |f $ %{} :Leaf (:at 1624244197845) (:by |S1lNv50FW) (:text |filter-ns) - |r $ %{} :Expr (:at 1624244160589) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |get) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |latest-files) + |X $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |filter-ns) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1624244160589) (:by |S1lNv50FW) (:text |if) - |j $ %{} :Expr (:at 1624244160589) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |if) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1624244160589) (:by |S1lNv50FW) (:text |some?) - |j $ %{} :Leaf (:at 1624244160589) (:by |S1lNv50FW) (:text |target) - |r $ %{} :Expr (:at 1624244160589) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |some?) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |target) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1624244160589) (:by |S1lNv50FW) (:text |assoc) - |j $ %{} :Leaf (:at 1624244212403) (:by |S1lNv50FW) (:text |old-files) - |r $ %{} :Leaf (:at 1624244223852) (:by |S1lNv50FW) (:text |filter-ns) - |v $ %{} :Leaf (:at 1624244160589) (:by |S1lNv50FW) (:text |target) - |v $ %{} :Expr (:at 1624244160589) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |assoc) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |old-files) + |X $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |filter-ns) + |Z $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |target) + |Z $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1624244160589) (:by |S1lNv50FW) (:text |dissoc) - |j $ %{} :Leaf (:at 1624244215917) (:by |S1lNv50FW) (:text |old-files) - |r $ %{} :Leaf (:at 1624244218442) (:by |S1lNv50FW) (:text |filter-ns) - |v $ %{} :Leaf (:at 1624244284175) (:by |S1lNv50FW) (:text |latest-files) - |T $ %{} :Expr (:at 1596297788745) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |dissoc) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |old-files) + |X $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |filter-ns) + |Z $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |latest-files) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596336866754) (:by |S1lNv50FW) (:text |compact-data) - |j $ %{} :Expr (:at 1596301231222) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |compact-data) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1596301231786) (:by |S1lNv50FW) (:text |{}) - |L $ %{} :Expr (:at 1596301232097) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |{}) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596301233742) (:by |S1lNv50FW) (:text |:package) - |j $ %{} :Leaf (:at 1596343144643) (:by |S1lNv50FW) (:text |pkg) - |N $ %{} :Expr (:at 1599733075547) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:package) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |pkg) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1599733076861) (:by |S1lNv50FW) (:text |:configs) - |j $ %{} :Expr (:at 1599733281290) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:about) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text "|\"file is generated - never edit directly; learn cr edit/tree workflows before changing") + |Z $ %{} :Expr (:at 1767779797416) (:by |sync) + :data $ {} + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:configs) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1599733281949) (:by |S1lNv50FW) (:text |{}) - |T $ %{} :Expr (:at 1599733282461) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |{}) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1599733285795) (:by |S1lNv50FW) (:text |:init-fn) - |T $ %{} :Expr (:at 1599733286881) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:init-fn) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1599733288405) (:by |S1lNv50FW) (:text |:init-fn) - |T $ %{} :Leaf (:at 1599733152715) (:by |S1lNv50FW) (:text |configs) - |j $ %{} :Expr (:at 1599733282461) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:init-fn) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |configs) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1599733296310) (:by |S1lNv50FW) (:text |:reload-fn) - |T $ %{} :Expr (:at 1599733286881) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:reload-fn) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1599733297959) (:by |S1lNv50FW) (:text |:reload-fn) - |T $ %{} :Leaf (:at 1599733152715) (:by |S1lNv50FW) (:text |configs) - |r $ %{} :Expr (:at 1606286816878) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:reload-fn) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |configs) + |Z $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1606286821525) (:by |S1lNv50FW) (:text |:modules) - |j $ %{} :Expr (:at 1606286822071) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:modules) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1606286829014) (:by |S1lNv50FW) (:text |:modules) - |j $ %{} :Leaf (:at 1606286831681) (:by |S1lNv50FW) (:text |configs) - |v $ %{} :Expr (:at 1606792829464) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:modules) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |configs) + |b $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1606792830583) (:by |S1lNv50FW) (:text |:version) - |j $ %{} :Expr (:at 1606792831019) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:version) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1606792832167) (:by |S1lNv50FW) (:text |:version) - |j $ %{} :Leaf (:at 1606792833074) (:by |S1lNv50FW) (:text |configs) - |O $ %{} :Expr (:at 1636634863890) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:version) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |configs) + |b $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1636634863890) (:by |S1lNv50FW) (:text |:entries) - |j $ %{} :Leaf (:at 1636635191916) (:by |S1lNv50FW) (:text |entries) - |P $ %{} :Expr (:at 1596301237723) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:entries) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |entries) + |d $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596301240636) (:by |S1lNv50FW) (:text |:files) - |j $ %{} :Expr (:at 1596301240846) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:files) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625769200610) (:by |S1lNv50FW) (:text |->) - |j $ %{} :Leaf (:at 1596343155785) (:by |S1lNv50FW) (:text |new-files) - |p $ %{} :Expr (:at 1625684393094) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |->) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |new-files) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625684397599) (:by |S1lNv50FW) (:text |map-kv) - |j $ %{} :Expr (:at 1625684393897) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |map-kv) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625684394166) (:by |S1lNv50FW) (:text |fn) - |j $ %{} :Expr (:at 1625684394474) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |fn) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625684394701) (:by |S1lNv50FW) (:text |k) - |j $ %{} :Leaf (:at 1625684395559) (:by |S1lNv50FW) (:text |v) - |r $ %{} :Expr (:at 1625684401304) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |k) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |v) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625684402069) (:by |S1lNv50FW) (:text |[]) - |j $ %{} :Leaf (:at 1625684403216) (:by |S1lNv50FW) (:text |k) - |r $ %{} :Expr (:at 1625684405046) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |[]) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |k) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625684405046) (:by |S1lNv50FW) (:text |file->cirru) - |j $ %{} :Leaf (:at 1625684407401) (:by |S1lNv50FW) (:text |v) - |n $ %{} :Leaf (:at 1758954363297) (:by |S1lNv50FW) (:text |true) - |j $ %{} :Expr (:at 1596336845843) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |file->cirru) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |v) + |X $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |true) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596336850628) (:by |S1lNv50FW) (:text |inc-data) - |j $ %{} :Expr (:at 1596345169187) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |inc-data) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1596345170865) (:by |S1lNv50FW) (:text |hide-empty-fields) - |T $ %{} :Expr (:at 1596336852094) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |hide-empty-fields) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596336873097) (:by |S1lNv50FW) (:text |{}) - |b $ %{} :Expr (:at 1596343230908) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |{}) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343230908) (:by |S1lNv50FW) (:text |:removed) - |j $ %{} :Leaf (:at 1596343230908) (:by |S1lNv50FW) (:text |removed-names) - |j $ %{} :Expr (:at 1596336873849) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:removed) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |removed-names) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596336874833) (:by |S1lNv50FW) (:text |:added) - |j $ %{} :Expr (:at 1596336912259) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:added) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1625725687471) (:by |S1lNv50FW) (:text |->) - |T $ %{} :Leaf (:at 1596336879879) (:by |S1lNv50FW) (:text |added-names) - |j $ %{} :Expr (:at 1596336915703) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |->) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |added-names) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625725695382) (:by |S1lNv50FW) (:text |map) - |j $ %{} :Expr (:at 1596336916915) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |map) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596336917178) (:by |S1lNv50FW) (:text |fn) - |j $ %{} :Expr (:at 1596336917436) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |fn) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596336919972) (:by |S1lNv50FW) (:text |ns-text) - |r $ %{} :Expr (:at 1596343315550) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |ns-text) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1596343316054) (:by |S1lNv50FW) (:text |[]) - |L $ %{} :Leaf (:at 1596343318650) (:by |S1lNv50FW) (:text |ns-text) - |T $ %{} :Expr (:at 1596343314610) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |[]) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |ns-text) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343314610) (:by |S1lNv50FW) (:text |file->cirru) - |j $ %{} :Expr (:at 1596343314610) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |file->cirru) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343323593) (:by |S1lNv50FW) (:text |get) - |j $ %{} :Leaf (:at 1596343321832) (:by |S1lNv50FW) (:text |new-files) - |r $ %{} :Leaf (:at 1596343314610) (:by |S1lNv50FW) (:text |ns-text) - |n $ %{} :Leaf (:at 1758954369712) (:by |S1lNv50FW) (:text |true) - |r $ %{} :Expr (:at 1596336944822) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |get) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |new-files) + |X $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |ns-text) + |X $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |true) + |Z $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625725700083) (:by |S1lNv50FW) (:text |pairs-map) - |v $ %{} :Expr (:at 1596336890078) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |pairs-map) + |Z $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596336902916) (:by |S1lNv50FW) (:text |:changed) - |j $ %{} :Expr (:at 1596336948919) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:changed) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625769204036) (:by |S1lNv50FW) (:text |->) - |j $ %{} :Leaf (:at 1596336951838) (:by |S1lNv50FW) (:text |changed-names) - |r $ %{} :Expr (:at 1596336948919) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |->) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |changed-names) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596336948919) (:by |S1lNv50FW) (:text |map) - |j $ %{} :Expr (:at 1596336948919) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |map) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596336948919) (:by |S1lNv50FW) (:text |fn) - |j $ %{} :Expr (:at 1596336948919) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |fn) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596336948919) (:by |S1lNv50FW) (:text |ns-text) - |r $ %{} :Expr (:at 1596336948919) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |ns-text) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596336948919) (:by |S1lNv50FW) (:text |[]) - |j $ %{} :Leaf (:at 1596336948919) (:by |S1lNv50FW) (:text |ns-text) - |r $ %{} :Expr (:at 1596343384023) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |[]) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |ns-text) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1596343385649) (:by |S1lNv50FW) (:text |let) - |L $ %{} :Expr (:at 1596343386055) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |let) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Expr (:at 1596343386732) (:by |S1lNv50FW) + |T $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343395269) (:by |S1lNv50FW) (:text |old-file) - |j $ %{} :Expr (:at 1596343397680) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |old-file) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343397680) (:by |S1lNv50FW) (:text |get) - |j $ %{} :Leaf (:at 1596343397680) (:by |S1lNv50FW) (:text |old-files) - |r $ %{} :Leaf (:at 1596343397680) (:by |S1lNv50FW) (:text |ns-text) - |j $ %{} :Expr (:at 1596343400497) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |get) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |old-files) + |X $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |ns-text) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343402039) (:by |S1lNv50FW) (:text |new-file) - |j $ %{} :Expr (:at 1596343405732) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |new-file) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343405732) (:by |S1lNv50FW) (:text |get) - |j $ %{} :Leaf (:at 1596343405732) (:by |S1lNv50FW) (:text |new-files) - |r $ %{} :Leaf (:at 1596343405732) (:by |S1lNv50FW) (:text |ns-text) - |r $ %{} :Expr (:at 1596343569630) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |get) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |new-files) + |X $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |ns-text) + |X $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343569630) (:by |S1lNv50FW) (:text |old-defs) - |j $ %{} :Expr (:at 1596343569630) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |old-defs) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343569630) (:by |S1lNv50FW) (:text |:defs) - |j $ %{} :Leaf (:at 1596343569630) (:by |S1lNv50FW) (:text |old-file) - |v $ %{} :Expr (:at 1596343569630) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:defs) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |old-file) + |Z $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343569630) (:by |S1lNv50FW) (:text |new-defs) - |j $ %{} :Expr (:at 1596343569630) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |new-defs) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343569630) (:by |S1lNv50FW) (:text |:defs) - |j $ %{} :Leaf (:at 1596343569630) (:by |S1lNv50FW) (:text |new-file) - |w $ %{} :Expr (:at 1596344208264) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |:defs) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |new-file) + |b $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596344211706) (:by |S1lNv50FW) (:text |old-def-names) - |j $ %{} :Expr (:at 1596344370330) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |old-def-names) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1596344370967) (:by |S1lNv50FW) (:text |keys) - |T $ %{} :Leaf (:at 1596344225784) (:by |S1lNv50FW) (:text |old-defs) - |wT $ %{} :Expr (:at 1596344208264) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |keys) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |old-defs) + |d $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596344220159) (:by |S1lNv50FW) (:text |new-def-names) - |j $ %{} :Expr (:at 1596344374144) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |new-def-names) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1596344376680) (:by |S1lNv50FW) (:text |keys) - |T $ %{} :Leaf (:at 1596344228964) (:by |S1lNv50FW) (:text |new-defs) - |x $ %{} :Expr (:at 1596343569630) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |keys) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |new-defs) + |f $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343569630) (:by |S1lNv50FW) (:text |added-defs) - |j $ %{} :Expr (:at 1596343569630) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |added-defs) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343569630) (:by |S1lNv50FW) (:text |difference) - |j $ %{} :Leaf (:at 1596344236380) (:by |S1lNv50FW) (:text |new-def-names) - |r $ %{} :Leaf (:at 1596344239639) (:by |S1lNv50FW) (:text |old-def-names) - |y $ %{} :Expr (:at 1596343569630) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |difference) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |new-def-names) + |X $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |old-def-names) + |h $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343569630) (:by |S1lNv50FW) (:text |removed-defs) - |j $ %{} :Expr (:at 1596343569630) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |removed-defs) + |V $ %{} :Expr (:at 1767779797416) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343569630) (:by |S1lNv50FW) (:text |difference) - |j $ %{} :Leaf (:at 1596344245148) (:by |S1lNv50FW) (:text |old-def-names) - |r $ %{} :Leaf (:at 1596344247206) (:by |S1lNv50FW) (:text |new-def-names) - |yT $ %{} :Expr (:at 1596343647528) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |difference) + |V $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |old-def-names) + |X $ %{} :Leaf (:at 1767779797416) (:by |sync) (:text |new-def-names) + |j $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343647813) (:by |S1lNv50FW) (:text |changed-defs) - |j $ %{} :Expr (:at 1596343665709) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |changed-defs) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1625769206584) (:by |S1lNv50FW) (:text |->) - |T $ %{} :Expr (:at 1596343648793) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |->) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343659021) (:by |S1lNv50FW) (:text |intersection) - |j $ %{} :Leaf (:at 1596344251289) (:by |S1lNv50FW) (:text |old-def-names) - |r $ %{} :Leaf (:at 1596344254062) (:by |S1lNv50FW) (:text |new-def-names) - |j $ %{} :Expr (:at 1596343667136) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |intersection) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |old-def-names) + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |new-def-names) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343671369) (:by |S1lNv50FW) (:text |filter) - |j $ %{} :Expr (:at 1596343671705) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |filter) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343672233) (:by |S1lNv50FW) (:text |fn) - |j $ %{} :Expr (:at 1596343672829) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |fn) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343673606) (:by |S1lNv50FW) (:text |x) - |r $ %{} :Expr (:at 1596343674590) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |x) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343693432) (:by |S1lNv50FW) (:text |not=) - |j $ %{} :Expr (:at 1596343694188) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |not=) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343695884) (:by |S1lNv50FW) (:text |get) - |j $ %{} :Leaf (:at 1596343699311) (:by |S1lNv50FW) (:text |old-defs) - |r $ %{} :Leaf (:at 1596343700027) (:by |S1lNv50FW) (:text |x) - |r $ %{} :Expr (:at 1596343694188) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |get) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |old-defs) + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |x) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343695884) (:by |S1lNv50FW) (:text |get) - |j $ %{} :Leaf (:at 1596343706091) (:by |S1lNv50FW) (:text |new-defs) - |r $ %{} :Leaf (:at 1596343700027) (:by |S1lNv50FW) (:text |x) - |T $ %{} :Expr (:at 1596345047977) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |get) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |new-defs) + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |x) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1596345048636) (:by |S1lNv50FW) (:text |hide-empty-fields) - |T $ %{} :Expr (:at 1596343331521) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |hide-empty-fields) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343331882) (:by |S1lNv50FW) (:text |{}) - |j $ %{} :Expr (:at 1596343332074) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |{}) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343332521) (:by |S1lNv50FW) (:text |:ns) - |j $ %{} :Expr (:at 1596343340573) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:ns) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343340944) (:by |S1lNv50FW) (:text |if) - |j $ %{} :Expr (:at 1596343350068) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |if) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343354879) (:by |S1lNv50FW) (:text |=) - |f $ %{} :Expr (:at 1596343407843) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |=) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343408473) (:by |S1lNv50FW) (:text |:ns) - |j $ %{} :Leaf (:at 1596343410666) (:by |S1lNv50FW) (:text |old-file) - |p $ %{} :Expr (:at 1596343414538) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:ns) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |old-file) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343414980) (:by |S1lNv50FW) (:text |:ns) - |j $ %{} :Leaf (:at 1596343417160) (:by |S1lNv50FW) (:text |new-file) - |r $ %{} :Leaf (:at 1596343367493) (:by |S1lNv50FW) (:text |nil) - |v $ %{} :Expr (:at 1625831090431) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:ns) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |new-file) + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |nil) + |Z $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |5 $ %{} :Leaf (:at 1625831092363) (:by |S1lNv50FW) (:text |::) - |D $ %{} :Leaf (:at 1625831091151) (:by |S1lNv50FW) (:text |'quote) - |T $ %{} :Expr (:at 1596343368266) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |::) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |'quote) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1605582228019) (:by |S1lNv50FW) (:text |tree->cirru) - |j $ %{} :Expr (:at 1692519105565) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |tree->cirru) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1692519107839) (:by |S1lNv50FW) (:text |:code) - |T $ %{} :Expr (:at 1596343423979) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:code) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343423979) (:by |S1lNv50FW) (:text |:ns) - |j $ %{} :Leaf (:at 1596343423979) (:by |S1lNv50FW) (:text |new-file) - |t $ %{} :Expr (:at 1596343586024) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:ns) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |new-file) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343588380) (:by |S1lNv50FW) (:text |:removed-defs) - |j $ %{} :Leaf (:at 1596343590566) (:by |S1lNv50FW) (:text |removed-defs) - |v $ %{} :Expr (:at 1596343334380) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:removed-defs) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |removed-defs) + |Z $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343575859) (:by |S1lNv50FW) (:text |:added-defs) - |j $ %{} :Expr (:at 1596343580465) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:added-defs) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625769208184) (:by |S1lNv50FW) (:text |->) - |j $ %{} :Leaf (:at 1596343584391) (:by |S1lNv50FW) (:text |added-defs) - |r $ %{} :Expr (:at 1596343592731) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |->) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |added-defs) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343593790) (:by |S1lNv50FW) (:text |map) - |j $ %{} :Expr (:at 1596343594065) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |map) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343595940) (:by |S1lNv50FW) (:text |fn) - |j $ %{} :Expr (:at 1596343596353) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |fn) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343600684) (:by |S1lNv50FW) (:text |x) - |r $ %{} :Expr (:at 1596343602072) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |x) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343603121) (:by |S1lNv50FW) (:text |[]) - |j $ %{} :Leaf (:at 1596343603580) (:by |S1lNv50FW) (:text |x) - |r $ %{} :Expr (:at 1758952730251) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |[]) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |x) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |let) - |b $ %{} :Expr (:at 1758952730251) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |let) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Expr (:at 1758952730251) (:by |S1lNv50FW) + |T $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |extracted) - |b $ %{} :Expr (:at 1758952730251) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |extracted) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |extract-examples-code) - |b $ %{} :Expr (:at 1758952730251) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |extract-examples-code) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |tree->cirru) - |b $ %{} :Expr (:at 1758952730251) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |tree->cirru) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |get-in) - |b $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |new-defs) - |h $ %{} :Expr (:at 1758952730251) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |get-in) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |new-defs) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |[]) - |b $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |x) - |h $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |:code) - |h $ %{} :Expr (:at 1758952730251) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |[]) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |x) + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:code) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |tag-match) - |b $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |extracted) - |h $ %{} :Expr (:at 1758952730251) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |tag-match) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |extracted) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Expr (:at 1758952730251) (:by |S1lNv50FW) + |T $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |:exp-code) - |b $ %{} :Leaf (:at 1758954903349) (:by |S1lNv50FW) (:text |_e) - |h $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |code) - |b $ %{} :Expr (:at 1758952730251) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:exp-code) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |_e) + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |code) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |::) - |b $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |'quote) - |h $ %{} :Leaf (:at 1758952730251) (:by |S1lNv50FW) (:text |code) - |x $ %{} :Expr (:at 1596345055037) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |::) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |'quote) + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |code) + |Z $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596345055660) (:by |S1lNv50FW) (:text |hide-empty-fields) - |x $ %{} :Expr (:at 1596343334380) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |hide-empty-fields) + |b $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343637354) (:by |S1lNv50FW) (:text |:changed-defs) - |j $ %{} :Expr (:at 1596343580465) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:changed-defs) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625769209831) (:by |S1lNv50FW) (:text |->) - |j $ %{} :Leaf (:at 1596343644169) (:by |S1lNv50FW) (:text |changed-defs) - |r $ %{} :Expr (:at 1596343592731) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |->) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |changed-defs) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343593790) (:by |S1lNv50FW) (:text |map) - |j $ %{} :Expr (:at 1596343594065) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |map) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343595940) (:by |S1lNv50FW) (:text |fn) - |j $ %{} :Expr (:at 1596343596353) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |fn) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343600684) (:by |S1lNv50FW) (:text |x) - |r $ %{} :Expr (:at 1596343602072) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |x) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596343603121) (:by |S1lNv50FW) (:text |[]) - |j $ %{} :Leaf (:at 1596343603580) (:by |S1lNv50FW) (:text |x) - |r $ %{} :Expr (:at 1758952751620) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |[]) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |x) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |let) - |b $ %{} :Expr (:at 1758952751620) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |let) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Expr (:at 1758952751620) (:by |S1lNv50FW) + |T $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |extracted) - |b $ %{} :Expr (:at 1758952751620) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |extracted) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |extract-examples-code) - |b $ %{} :Expr (:at 1758952751620) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |extract-examples-code) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |tree->cirru) - |b $ %{} :Expr (:at 1758952751620) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |tree->cirru) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |get-in) - |b $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |new-defs) - |h $ %{} :Expr (:at 1758952751620) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |get-in) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |new-defs) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |[]) - |b $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |x) - |h $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |:code) - |h $ %{} :Expr (:at 1758952751620) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |[]) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |x) + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:code) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |tag-match) - |b $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |extracted) - |h $ %{} :Expr (:at 1758952751620) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |tag-match) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |extracted) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Expr (:at 1758952751620) (:by |S1lNv50FW) + |T $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |:exp-code) - |b $ %{} :Leaf (:at 1758954905477) (:by |S1lNv50FW) (:text |_e) - |h $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |code) - |b $ %{} :Expr (:at 1758952751620) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |:exp-code) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |_e) + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |code) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |::) - |b $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |'quote) - |h $ %{} :Leaf (:at 1758952751620) (:by |S1lNv50FW) (:text |code) - |v $ %{} :Expr (:at 1596345062653) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |::) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |'quote) + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |code) + |Z $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596345060672) (:by |S1lNv50FW) (:text |hide-empty-fields) - |v $ %{} :Expr (:at 1596336948919) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |hide-empty-fields) + |Z $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625725711700) (:by |S1lNv50FW) (:text |pairs-map) - |r $ %{} :Expr (:at 1596297794059) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |pairs-map) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596297798768) (:by |S1lNv50FW) (:text |fs/writeFile) - |j $ %{} :Leaf (:at 1596297806859) (:by |S1lNv50FW) (:text "|\"compact.cirru") - |r $ %{} :Expr (:at 1596336859434) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |fs/writeFile) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text "|\"compact.cirru") + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1625684558461) (:by |S1lNv50FW) (:text |format-cirru-edn) - |T $ %{} :Leaf (:at 1596336869216) (:by |S1lNv50FW) (:text |compact-data) - |v $ %{} :Expr (:at 1596297979948) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |format-cirru-edn) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |compact-data) + |Z $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596297980050) (:by |S1lNv50FW) (:text |fn) - |j $ %{} :Expr (:at 1596297981965) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |fn) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596297982528) (:by |S1lNv50FW) (:text |err) - |r $ %{} :Expr (:at 1596297983809) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |err) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596297984273) (:by |S1lNv50FW) (:text |if) - |j $ %{} :Expr (:at 1596297984640) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |if) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |D $ %{} :Leaf (:at 1596297989113) (:by |S1lNv50FW) (:text |some?) - |T $ %{} :Leaf (:at 1596297986036) (:by |S1lNv50FW) (:text |err) - |r $ %{} :Expr (:at 1596297989570) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |some?) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |err) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596297995062) (:by |S1lNv50FW) (:text |js/console.log) - |j $ %{} :Leaf (:at 1596298003929) (:by |S1lNv50FW) (:text "|\"Failed to write!") - |r $ %{} :Leaf (:at 1596298004710) (:by |S1lNv50FW) (:text |err) - |v $ %{} :Expr (:at 1596297807853) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |js/console.log) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text "|\"Failed to write!") + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |err) + |Z $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596297810530) (:by |S1lNv50FW) (:text |fs/writeFile) - |j $ %{} :Leaf (:at 1596297818129) (:by |S1lNv50FW) (:text "|\".compact-inc.cirru") - |r $ %{} :Expr (:at 1596336954986) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |fs/writeFile) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text "|\".compact-inc.cirru") + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1625684560611) (:by |S1lNv50FW) (:text |format-cirru-edn) - |j $ %{} :Leaf (:at 1596336961780) (:by |S1lNv50FW) (:text |inc-data) - |v $ %{} :Expr (:at 1596298008085) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |format-cirru-edn) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |inc-data) + |Z $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596298008085) (:by |S1lNv50FW) (:text |fn) - |j $ %{} :Expr (:at 1596298008085) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |fn) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596298008085) (:by |S1lNv50FW) (:text |err) - |r $ %{} :Expr (:at 1596298008085) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |err) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596298008085) (:by |S1lNv50FW) (:text |if) - |j $ %{} :Expr (:at 1596298008085) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |if) + |V $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596298008085) (:by |S1lNv50FW) (:text |some?) - |j $ %{} :Leaf (:at 1596298008085) (:by |S1lNv50FW) (:text |err) - |r $ %{} :Expr (:at 1596298008085) (:by |S1lNv50FW) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |some?) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |err) + |X $ %{} :Expr (:at 1767779797417) (:by |sync) :data $ {} - |T $ %{} :Leaf (:at 1596298008085) (:by |S1lNv50FW) (:text |js/console.log) - |j $ %{} :Leaf (:at 1596298008085) (:by |S1lNv50FW) (:text "|\"Failed to write!") - |r $ %{} :Leaf (:at 1596298008085) (:by |S1lNv50FW) (:text |err) + |T $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |js/console.log) + |V $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text "|\"Failed to write!") + |X $ %{} :Leaf (:at 1767779797417) (:by |sync) (:text |err) :examples $ [] |handle-files! $ %{} :CodeEntry (:doc |) :code $ %{} :Expr (:at 1504777570689) (:by |) diff --git a/compact.cirru b/compact.cirru index cf161a1..702c8eb 100644 --- a/compact.cirru +++ b/compact.cirru @@ -1,9 +1,9 @@ -{} (:package |app) - :configs $ {} (:init-fn |app.server/main!) (:reload-fn |app.server/reload!) (:version |0.9.11) +{} (:about "|file is generated - never edit directly; learn cr edit/tree workflows before changing") (:package |app) + :configs $ {} (:init-fn |app.server/main!) (:reload-fn |app.server/reload!) (:version |0.9.12) :modules $ [] |lilac/ |memof/ |recollect/ |cumulo-util.calcit/ |ws-edn.calcit/ |bisection-key/ |respo-markdown.calcit/ :entries $ {} - :client $ {} (:init-fn |app.client/main!) (:reload-fn |app.client/reload!) + :client $ {} (:init-fn |app.client/main!) (:reload-fn |app.client/reload!) (:version |0.0.0) :modules $ [] |lilac/ |memof/ |recollect/ |respo.calcit/ |respo-ui.calcit/ |respo-message.calcit/ |cumulo-util.calcit/ |ws-edn.calcit/ |respo-feather.calcit/ |alerts.calcit/ |respo-markdown.calcit/ |bisection-key/ |gen-code/ :files $ {} |app.bookmark $ %{} :FileEntry @@ -6608,7 +6608,7 @@ target $ get latest-files filter-ns if (some? target) (assoc old-files filter-ns target) (dissoc old-files filter-ns) , latest-files - compact-data $ {} (:package pkg) + compact-data $ {} (:package pkg) (:about "\"file is generated - never edit directly; learn cr edit/tree workflows before changing") :configs $ {} :init-fn $ :init-fn configs :reload-fn $ :reload-fn configs diff --git a/deps.cirru b/deps.cirru index 7f5fe98..27745b9 100644 --- a/deps.cirru +++ b/deps.cirru @@ -1,15 +1,15 @@ {} (:calcit-version |0.10.4) :dependencies $ {} (|Cumulo/cumulo-util.calcit |main) - |Respo/alerts.calcit |0.10.2 + |Respo/alerts.calcit |0.10.3 |Respo/respo-feather.calcit |main |Respo/respo-markdown.calcit |0.4.11 - |Respo/respo-message.calcit |main + |Respo/respo-message.calcit |0.0.9 |Respo/respo-ui.calcit |0.6.3 - |Respo/respo.calcit |0.16.21 + |Respo/respo.calcit |0.16.24 |calcit-lang/bisection-key |0.0.16 |calcit-lang/gen-code |0.0.5 |calcit-lang/lilac |main - |calcit-lang/memof |main - |calcit-lang/recollect |main - |mvc-works/ws-edn.calcit |0.0.10 + |calcit-lang/memof |0.0.17 + |calcit-lang/recollect |0.0.17 + |mvc-works/ws-edn.calcit |0.0.11 diff --git a/llms/Calcit.md b/llms/Calcit.md new file mode 100644 index 0000000..ff9ff12 --- /dev/null +++ b/llms/Calcit.md @@ -0,0 +1,836 @@ +# Calcit 编程 Agent 指南 + +本文档为 AI Agent 提供 Calcit 项目的操作指南。 + +## ⚠️ 重要警告:禁止直接修改的文件 + +以下文件**严格禁止使用文本替换或直接编辑**: + +- **`calcit.cirru`** - 这是 calcit-editor 结构化编辑器的专用格式,包含完整的编辑器元数据 +- **`compact.cirru`** - 这是 Calcit 程序的紧凑快照格式,必须使用 `cr edit` 相关命令进行修改 + +这两个文件的格式对空格和结构极其敏感,直接文本修改会破坏文件结构。请使用下面文档中的 CLI 命令进行代码查询和修改。 + +## Calcit 与 Cirru 的关系 + +- **Calcit** 是编程语言本身(一门类似 Clojure 的函数式编程语言) +- **Cirru** 是语法格式(缩进风格的 S-expression,类似去掉括号改用缩进的 Lisp) +- **关系**:Calcit 代码使用 Cirru 语法书写和存储 + +**具体体现:** + +- `compact.cirru` 和 `calcit.cirru` 是用 Cirru 格式存储的 Calcit 程序 +- `cr cirru` 工具用于 Cirru 语法与 JSON 的转换(帮助理解和生成代码) +- Cirru 语法特点: + - 用缩进代替括号(类似 Python/YAML) + - 字符串用前缀 `|` 或 `"` 标记(如 `|hello` 表示字符串 "hello") + - 单行用空格分隔元素(如 `defn add (a b) (+ a b)`) + +**类比理解:** + +- Python 语言 ← 使用 → Python 语法 +- Calcit 语言 ← 使用 → Cirru 语法 + +生成 Calcit 代码前,建议先运行 `cr cirru show-guide` 了解 Cirru 语法规则。 + +--- + +## Calcit CLI 命令 + +Calcit 程序使用 `cr` 命令: + +### 主要运行命令 + +- `cr` 或 `cr compact.cirru` - 代码解释执行,默认读取 config 执行 init-fn 定义的入口 +- `cr compact.cirru js` - 编译生成 JavaScript 代码 +- `cr -1 ` - 执行一次然后退出(不进入监听模式) +- `cr --check-only` - 仅检查代码正确性,不执行程序 + - 对 init_fn 和 reload_fn 进行预处理验证 + - 输出:预处理进度、warnings、检查耗时 + - 用于 CI/CD 或快速验证代码修改 +- `cr js -1` - 检查代码正确性,生成 JavaScript(不进入监听模式) +- `cr js --check-only` - 检查代码正确性,不生成 JavaScript +- `cr eval ''` - 执行一段 Calcit 代码片段,用于快速验证写法 + +### 查询子命令 (`cr query`) + +这些命令用于查询项目信息: + +**项目全局分析:** + +- `cr analyze call-graph` - 分析从入口点开始的调用图结构 +- `cr analyze count-calls` - 统计每个定义的调用次数 + + _使用示例:_ + + ```bash + # 分析整个项目的调用图 + cr analyze call-graph + # 统计调用次数 + cr analyze count-calls + ``` + +**基础查询:** + +- `cr query ns [--deps]` - 列出项目中所有命名空间(--deps 包含依赖) +- `cr query ns ` - 读取命名空间详情(imports, 定义预览) +- `cr query defs ` - 列出命名空间中的定义 +- `cr query pkg` - 获取项目包名 +- `cr query config` - 读取项目配置(init_fn, reload_fn, version) +- `cr query error` - 读取 .calcit-error.cirru 错误堆栈文件 +- `cr query modules` - 列出项目模块 + +**渐进式代码探索(Progressive Disclosure):** + +- `cr query peek ` - 查看定义签名(参数、文档、表达式数量),不返回完整实现体 + - 输出:Doc、Form 类型、参数列表、Body 表达式数量、首个表达式预览、Examples 数量 + - 用于快速了解函数接口,减少 token 消耗 +- `cr query def ` - 读取定义的完整语法树(JSON 格式) + - 同时显示 Doc 和 Examples 的完整内容 +- `cr query examples ` - 读取定义的示例代码 + - 输出:每个 example 的 Cirru 格式和 JSON 格式 + +**符号搜索与引用分析:** + +- `cr query find [--deps] [-f] [-n ]` - 跨命名空间搜索符号 + - 默认精确匹配:返回定义位置 + 所有引用位置(带上下文预览) + - `-f` / `--fuzzy`:模糊搜索,匹配 "namespace/definition" 格式的路径 + - `-n `:限制模糊搜索结果数量(默认 20) + - `--deps`:包含核心命名空间(calcit.\* 开头) +- `cr query usages [--deps]` - 查找定义的所有使用位置 + - 返回:引用该定义的所有位置(带上下文预览) + - 用于理解代码影响范围,重构前的影响分析 + +**代码模式搜索:** + +- `cr query search [-f ] [-l] [-d ]` - 搜索叶子节点(字符串) + + - `` - 位置参数,要搜索的字符串模式 + - `-f` / `--filter` - 过滤到特定命名空间或定义(可选) + - `-l` / `--loose`:宽松匹配,包含模式(匹配所有包含该模式的叶子节点) + - `-d `:限制搜索深度(0 = 无限制) + - 返回:匹配节点的完整路径 + 父级上下文预览 + - 示例: + - `cr query search "println" -f app.main/main -l` - 在 main 函数中搜索包含 "println" 的节点 + - `cr query search "div"` - 全局精确搜索 "div" + +- `cr query search-pattern [-f ] [-l] [-j] [-d ]` - 搜索结构模式 + - `` - 位置参数,Cirru one-liner 或 JSON 数组模式 + - `-f` / `--filter` - 过滤到特定命名空间或定义(可选) + - `-l` / `--loose`:宽松匹配,查找包含连续子序列的结构 + - `-j` / `--json`:将模式解析为 JSON 数组而非 Cirru + - 返回:匹配节点的路径 + 父级上下文 + - 示例: + - `cr query search-pattern "(+ a b)" -f app.util/add` - 查找精确表达式 + - `cr query search-pattern '["defn"]' -f app.main/main -j -l` - 查找所有函数定义 + +**搜索结果格式:** + +- 输出格式:`[路径] in 父级上下文` +- 路径格式:`[索引1,索引2,...]` 表示从根节点到匹配节点的路径 +- 可配合 `cr tree show -p ""` 查看具体节点内容 + +### 文档子命令 (`cr docs`) + +查询 Calcit 语言文档(guidebook): + +- `cr docs search [-c ] [-f ]` - 按关键词搜索文档内容 + + - `-c ` - 显示匹配行的上下文行数(默认 5) + - `-f ` - 按文件名过滤搜索结果 + - 输出:匹配行及其上下文,带行号和高亮 + - 示例:`cr docs search "macro" -c 10` 或 `cr docs search "defn" -f macros.md` + +- `cr docs read [-s ] [-n ]` - 阅读指定文档 + + - `-s ` - 起始行号(默认 0) + - `-n ` - 读取行数(默认 80) + - 输出:文档内容、当前范围、是否有更多内容 + - 示例:`cr docs read macros.md` 或 `cr docs read intro.md -s 20 -n 30` + +- `cr docs list` - 列出所有可用文档 + +### Cirru 语法工具 (`cr cirru`) + +用于 Cirru 语法和 JSON 之间的转换: + +- `cr cirru parse ''` - 解析 Cirru 代码为 JSON +- `cr cirru format ''` - 格式化 JSON 为 Cirru 代码 +- `cr cirru parse-edn ''` - 解析 Cirru EDN 为 JSON +- `cr cirru show-guide` - 显示 Cirru 语法指南(帮助 LLM 生成正确的 Cirru 代码) + +**⚠️ 重要:生成 Cirru 代码前请先阅读语法指南** + +运行 `cr cirru show-guide` 获取完整的 Cirru 语法说明,包括: + +- `$` 操作符(单节点展开) +- `|` 前缀(字符串字面量), 这个是 Cirru 特殊的地方, 而不是直接用引号包裹 +- `,` 操作符(注释标记) +- `~` 和 `~@`(宏展开) +- 常见错误和避免方法 + +### 库管理 (`cr libs`) + +查询和了解 Calcit 官方库: + +- `cr libs` - 列出所有官方库 +- `cr libs search ` - 按关键词搜索库(搜索名称、描述、分类) +- `cr libs readme [-f ]` - 查看库的文档 + - 优先从本地 `~/.config/calcit/modules/` 读取 + - 本地不存在时从 GitHub 仓库获取 + - `-f` 参数可指定其他文档文件(如 `-f Skills.md`) + - 默认读取 `README.md` +- `cr libs scan-md ` - 扫描本地模块目录下的所有 `.md` 文件 + - 递归扫描子目录 + - 显示相对路径列表 +- `caps` - 安装/更新依赖 + +### 精细代码树操作 (`cr tree`) + +⚠️ **关键警告:路径索引动态变化** + +删除或插入节点后,同级后续节点的索引会自动改变。**必须从后往前操作**或**每次修改后重新搜索路径**。详见 [常见陷阱 #1](#1-路径索引动态变化问题-)。 + +提供对 AST 节点的低级精确操作,适用于需要精细控制的场景: + +**可用操作:** + +- `cr tree show -p ` - 查看指定路径的节点 + + - `-p ` - 节点路径,用逗号分隔索引(如 `"3,2,1"`),空字符串 `""` 表示根节点 + - `-d ` - 限制显示深度(0=无限,默认 2) + - **输出格式**:Cirru 缩进格式的代码树,`[索引]` 标注每个子节点位置 + - **使用技巧**: + - 先用 `-d 1` 查看顶层结构,再逐层深入 + - 路径中的索引对应输出中的 `[0]`, `[1]`, `[2]` 等标记 + - 配合 `cr query search` 快速定位目标节点 + +- `cr tree replace -p ` - 替换指定路径的节点 + + - **输入方式**(按推荐优先级): + + 1. `-e ''` - **最推荐**。支持 Cirru one-liner,也支持自动识别 JSON (如 `-e '["a"]'`)。 + 2. `-j ''` - 显式传入内联 JSON 字符串。 + 3. `-f ` - 从文件读取(默认 Cirru,加 `-J` 读 JSON)。 + 4. `-s` - 从 stdin 读取(默认 Cirru,加 `-J` 读 JSON)。 + + - **特殊参数**: + + - `--leaf` - 配合 `-e`/`-f`/`-s` 将输入视为 leaf 节点 + - 符号:`--leaf -e 'my-var'` + - 字符串:`--leaf -e '|text'`(实测最快捷,示例:`cr tree replace respo.app.core/new-fn -p "3,1" --leaf -e '|hello'`) + - `--refer-original ` - 在新代码中引用原节点(高级用法) + - `--refer-inner-branch ` - 引用原节点的内部分支 + - `--refer-inner-placeholder ` - 内部分支的占位符名 + + - **快速决策**: + - 替换表达式 → 用 `-e 'cirru one-liner'` + - 替换单个值 → 用 `--leaf -e ''`(避免被包装成 list) + +- `cr tree delete -p ` - 删除指定路径的节点 + + - `-p ` - 要删除的节点路径(完整路径,含最后索引) + - ⚠️ **副作用**:删除后,同级后续节点的索引会前移 + - **示例**:删除 `[3,2,1]` 后,原 `[3,2,2]` 变成 `[3,2,1]` + +- `cr tree insert-before -p ` - 在指定位置前插入节点 + + - `-p ` - 参考节点的路径(在此节点之前插入) + - 输入方式同 `replace`(`-e`, `-j`, `-f`, `-s`) + - **结果**:新节点占据当前索引,原节点及后续节点索引 +1 + +- `cr tree insert-after -p ` - 在指定位置后插入节点 + + - `-p ` - 参考节点的路径(在此节点之后插入) + - 输入方式同 `replace` + - **结果**:新节点占据下一个索引,原节点索引不变 + +- `cr tree insert-child -p ` - 插入为第一个子节点 + + - `-p ` - 父节点的路径 + - **结果**:新节点成为 `,0`,原有子节点索引全部 +1 + +- `cr tree append-child -p ` - 追加为最后一个子节点 + + - `-p ` - 父节点的路径 + - **结果**:新节点成为最后一个子节点,不影响其他节点索引 + - **推荐**:批量插入时优先用此命令(索引稳定) + +- `cr tree swap-next -p ` - 与下一个兄弟节点交换位置 + + - `-p ` - 当前节点路径 + - **结果**:当前节点索引 +1,下一个节点索引 -1 + - **应用场景**:调整参数顺序、移动代码位置 + +- `cr tree swap-prev -p ` - 与上一个兄弟节点交换位置 + + - `-p ` - 当前节点路径 + - **结果**:当前节点索引 -1,上一个节点索引 +1 + +- `cr tree wrap -p ` - 用新结构包装节点 + - `-p ` - 要包装的节点路径 + - 输入方式同 `replace`,需使用 `--refer-original ` 引用原节点 + - **示例**:将 `x` 包装成 `(+ x 1)` + ```bash + cr tree wrap app.main/fn -p "2,0" -e '+ $original 1' --refer-original original + ``` + +**实战示例:** + +```bash +# 场景 1:查看函数结构(渐进式) +cr tree show app.main/main! -p "" -d 1 # 先看顶层 +cr tree show app.main/main! -p "2" -d 2 # 深入第 3 个子节点 +cr tree show app.main/main! -p "2,1,0" # 查看具体节点 + +# 场景 2:替换符号 / 字符串(推荐用 --leaf) +cr tree replace app.main/add -p "2,0" --leaf -e '*' # 将 + 改成 * +cr tree replace app.main/greet -p "1" --leaf -e '|Hello' # 替换字符串 leaf + +# 场景 3:替换表达式 +cr tree replace app.main/main! -p "2" -e 'println |modified' + +# 场景 4:删除节点(注意索引变化) +cr query search "old-fn" -f app.main/main! # 找到 [3,2,5] +cr tree delete app.main/main! -p "3,2,5" + +# 场景 5:批量追加(安全方法) +cr tree append-child app.main/init! -p "2" -e 'step-1' +cr tree append-child app.main/init! -p "2" -e 'step-2' # 索引稳定 +cr tree append-child app.main/init! -p "2" -e 'step-3' + +# 场景 6:包装节点(添加函数调用) +cr tree wrap app.main/fn -p "3,1" -e 'str $old' --refer-original old +# 将 x 变成 (str x) + +# 场景 7:配合 search 定位后替换 +cr query search ".show" -f app.comp.home/comp-box -l +# 输出:[3,2,1,3,1,3,2,1,2,0] in .show confirm-plugin ... +cr tree show app.comp.home/comp-box -p "3,2,1,3,1,3,2,1,2,0" # 确认 +cr tree replace app.comp.home/comp-box -p "3,2,1,3,1,3,2,1,2,0" --leaf -e '|.visible' + +# demos/compact.cirru 实操(验证最新 CLI 输出) +# 目标:把 `respo.app.core/new-fn` 中的 `|hello` 暂时改成其他文案,再恢复 +cr query search "println" -f respo.app.core/new-fn -l +# → 找到路径 [3,0] / [3,1] +cr tree show respo.app.core/new-fn -p "3,1" -d 2 +# → CLI 会显示 Location/Type、JSON、下一步提示 +cr tree replace respo.app.core/new-fn -p "3,1" --leaf -e '|hello from tree demo' +# → 输出 `Preview: replace ...`、Before/After diff、Verify 提示 +cr tree replace respo.app.core/new-fn -p "3,1" --leaf -e '|hello' +# → 同样会给出 diff,确保 demos 恢复原状 +# 以上流程适合在 demos/compact.cirru 做沙盒操作,熟悉路径与 diff 提示 +``` + +**⚠️ 安全操作流程(必读)** + +**单次修改标准流程:** + +```bash +# 【步骤 1】模糊搜索快速定位 +cr query search "target" -f app.core/my-fn -l +# 输出:[3,2,1,5] in target parent-context ... + +# 【步骤 2】逐层确认路径正确(避免操作错误节点) +cr tree show app.core/my-fn -p "" -d 1 # 查看顶层,确认 [3] 存在 +cr tree show app.core/my-fn -p "3,2" -d 2 # 查看上级,确认 [3,2,1] 存在 +cr tree show app.core/my-fn -p "3,2,1,5" # 查看目标节点内容 + +# 【步骤 3】执行修改 +cr tree replace app.core/my-fn -p "3,2,1,5" --leaf -e '|new-value' + +# 【步骤 4】验证修改结果 +cr tree show app.core/my-fn -p "3,2,1,5" # 确认内容已更新 +cr --check-only # 语法检查 +``` + +**批量修改策略:** + +```bash +# ❌ 错误示范:连续删除(索引会变化) +cr tree delete app.main/fn -p "3,2,1" +cr tree delete app.main/fn -p "3,2,2" # 错误!路径已失效 + +# ✅ 正确方法 A:从后往前操作 +cr query search "pattern" -f app.main/fn # 记录所有路径 +cr tree delete app.main/fn -p "3,2,3" # 先删除索引大的 +cr tree delete app.main/fn -p "3,2,2" +cr tree delete app.main/fn -p "3,2,1" # 最后删除索引小的 + +# ✅ 正确方法 B:单次操作后立即重新搜索 +cr tree delete app.main/fn -p "3,2,1" +cr query search "pattern" -f app.main/fn # 重新获取路径 +cr tree delete app.main/fn -p "<新路径>" + +# ✅ 正确方法 C:整体重写定义 +cr query def app.main/fn > fn.json +# 编辑 fn.json +cr edit def app.main/fn -f fn.json -J +``` + +### 代码编辑 (`cr edit`) + +直接编辑 compact.cirru 项目代码,支持三种输入方式: + +- `--file ` 或 `-f ` - 从文件读取(默认 Cirru 格式,使用 `-J` 指定 JSON) +- `--json ` 或 `-j ` - 内联 JSON 字符串 +- `--stdin` 或 `-s` - 从标准输入读取(默认 Cirru 格式,使用 `-J` 指定 JSON) + +额外支持“内联代码”参数: + +- `--code ` 或 `-e `:直接在命令行里传入一段代码。 + - 默认按 **Cirru 单行表达式(one-liner)** 解析。 + - 如果输入“看起来像 JSON”(例如 `-e '"abc"'`,或 `-e '["a"]'` 这类 `[...]` 且包含 `"`),则会按 JSON 解析。 + - ⚠️ 当输入看起来像 JSON 但 JSON 不合法时,会直接报错(不会回退当成 Cirru one-liner)。 + +对 `--file/--stdin` 输入,还支持以下“格式开关”(与 `-J/--json-input` 类似): + +- `--leaf`:把输入当成 **leaf 节点**,直接使用 Cirru 符号或 `|text` 字符串,无需 JSON 引号。 + - 传入符号:`-e 'my-symbol'` + - 传入字符串:加 Cirru 字符串前缀 `|` 或 `"`,例如 `-e '|my string'` 或 `-e '"my string'` + +⚠️ 注意:这些开关彼此互斥(一次只用一个)。 + +**推荐简化规则(命令行更好写):** + +- **JSON(单行)**:优先用 `-j ''` 或 `-e ''`(不需要 `-J`)。 +- **Cirru 单行表达式**:用 `-e ''`(`-e` 默认按 one-liner 解析)。 +- **Cirru 多行缩进**:用 `-f file.cirru` 或 `-s`(stdin)。 +- `-J/--json-input` 主要用于 **file/stdin** 读入 JSON(如 `-f code.json -J` 或 `-s -J`)。 + +补充:`-e/--code` 只有在 `[...]` 内部包含 `"` 时才会自动按 JSON 解析(例如 `-e '["a"]'`)。 +像 `-e '[]'` / `-e '[ ]'` 会默认按 Cirru one-liner 处理;如果你需要“空 JSON 数组”,用显式 JSON:`-j '[]'`。 + +如果你想在命令行里明确“这段就是 JSON”,请用 `-j ''`(`-J` 是给 file/stdin 用的)。 + +**定义操作:** + +- `cr edit def ` - 添加或更新定义 +- `cr edit rm-def ` - 删除定义 +- `cr edit doc ''` - 更新定义的文档 +- `cr edit examples ` - 设置定义的示例代码(批量替换) +- `cr edit add-example ` - 添加单个示例 +- `cr edit rm-example ` - 删除指定索引的示例(0-based) + +**命名空间操作:** + +- `cr edit add-ns ` - 添加命名空间 +- `cr edit rm-ns ` - 删除命名空间 +- `cr edit imports ` - 更新导入规则(全量替换) +- `cr edit add-import ` - 添加单个 import 规则 +- `cr edit rm-import ` - 移除指定来源的 import 规则 +- `cr edit ns-doc ''` - 更新命名空间文档 + +**模块和配置:** + +- `cr edit add-module ` - 添加模块依赖 +- `cr edit rm-module ` - 删除模块依赖 +- `cr edit config ` - 设置配置(key: init-fn, reload-fn, version) + +使用 `--help` 参数了解详细的输入方式和参数选项。 + +--- + +## Calcit 语言基础 + +### Cirru 语法核心概念 + +**与其他 Lisp 的区别:** + +- **缩进语法**:用缩进代替括号(类似 Python/YAML),单行用空格分隔 +- **字符串前缀**:`|hello` 或 `"hello"` 表示字符串,`|` 前缀更简洁 +- **无方括号花括号**:只用圆括号概念(体现在 JSON 转换中),Cirru 文本层面无括号 + +**常见混淆点:** + +❌ **错误理解:** Calcit 字符串是 `"x"` → JSON 是 `"\"x\""` +✅ **正确理解:** Cirru `|x` → JSON `"x"`,Cirru `"x"` → JSON `"x"` + +**示例对照:** + +| Cirru 代码 | JSON 等价 | JavaScript 等价 | +| ---------------- | -------------------------------- | ------------------------ | +| `\|hello` | `"hello"` | `"hello"` | +| `"world"` | `"world"` | `"world"` | +| `\|a b c` | `"a b c"` | `"a b c"` | +| `fn (x) (+ x 1)` | `["fn", ["x"], ["+", "x", "1"]]` | `fn(x) { return x + 1 }` | + +### 数据结构:Tuple vs Vector + +Calcit 特有的两种序列类型: + +**Tuple (`::`)** - 不可变、用于模式匹配 + +```cirru +; 创建 tuple +:: :event/type data + +; 模式匹配 +tag-match event + (:event/click data) (handle-click data) + (:event/input text) (handle-input text) +``` + +**Vector (`[]`)** - 可变、用于列表操作 + +```cirru +; 创建 vector +[] item1 item2 item3 + +; DOM 列表 +div {} $ [] + button {} |Click + span {} |Text +``` + +**常见错误:** + +```cirru +; ❌ 错误:用 vector 传事件 +send-event! $ [] :clipboard/read text +; 报错:tag-match expected tuple + +; ✅ 正确:用 tuple +send-event! $ :: :clipboard/read text +``` + +### 其他易错点 + +比较容易犯的错误: + +- Calcit 中字符串通过前缀区分,`|` 和 `"` 开头表示字符串。`|x` 对应 JavaScript 字符串 `"x"`。产生 JSON 时注意不要重复包裹引号。 +- Calcit 采用 Cirru 缩进语法,可以理解成去掉跨行括号改用缩进的 Lisp 变种。用 `cr cirru parse` 和 `cr cirru format` 互相转化试验。 +- Calcit 跟 Clojure 在语义上比较像,但语法层面只用圆括号,不用方括号花括号。 + +--- + +## 开发调试 + +简单脚本用 `cr -1 ` 直接执行。编译 JavaScript 用 `cr -1 js` 执行一次编译。 + +Calcit snapshot 文件中 config 有 `init-fn` 和 `reload-fn` 配置: + +- 初次启动调用 `init-fn` +- 每次修改代码后调用 `reload-fn` + +**典型开发流程:** + +```bash +# 1. 检查代码正确性 +cr --check-only + +# 2. 执行程序(一次性) +cr -1 + +# 3. 编译 JavaScript(一次性) +cr -1 js + +# 4. 进入监听模式开发 +cr # 解释执行模式 +cr js # JS 编译模式 +``` + +--- + +## 文档支持 + +遇到疑问时使用: + +- `cr docs search ` - 搜索 Calcit 教程内容 +- `cr docs read ` - 阅读完整文档 +- `cr docs list` - 查看所有可用文档 +- `cr query ns ` - 查看命名空间说明和函数文档 +- `cr query peek ` - 快速查看定义签名 +- `cr query def ` - 读取完整语法树 +- `cr query examples ` - 查看示例代码 +- `cr query find ` - 跨命名空间搜索符号 +- `cr query usages ` - 查找定义的使用位置 +- `cr query search -p ` - 搜索叶子节点 +- `cr query search-pattern -p ` - 搜索结构模式 +- `cr query error` - 查看最近的错误堆栈 + +--- + +## 代码修改示例 + +**添加新函数:** + +```bash +# Cirru one liner +cr edit def app.core/multiply -e 'defn multiply (x y) (* x y)' +# or JSON +cr edit def app.core/multiply -j '["defn", "multiply", ["x", "y"], ["*", "x", "y"]]' +``` + +**更新文档和示例:** + +```bash +# 更新文档 +cr edit doc app.core/multiply '乘法函数,返回两个数的积' + +# 设置示例 +cr edit examples app.core/multiply -j '[["multiply", "3", "4"]]' + +# 添加示例 +cr edit add-example app.core/multiply -e 'multiply 5 6' + +# 删除示例 +cr edit rm-example app.core/multiply 1 +``` + +**局部修改(推荐流程):** + +```bash +# 1. 读取完整定义 +cr query def app.core/add-numbers + +# 2. 多次查看节点确认目标坐标 +cr tree show app.core/add-numbers -p "" -d 1 +cr tree show app.core/add-numbers -p "2" -d 1 +cr tree show app.core/add-numbers -p "2,0" + +# 3. 执行替换 +cr tree replace app.core/add-numbers -p "2,0" -e '*' + +# 4. 验证 +cr tree show app.core/add-numbers -p "2" +``` + +**命名空间增量操作:** + +```bash +# 添加命名空间 +cr edit add-ns app.util + +# 添加导入规则 +cr edit add-import app.main -e 'app.util :refer $ helper' + +# 移除导入规则 +cr edit rm-import app.main app.util + +# 更新项目配置 +cr edit config init-fn app.main/main! +``` + +**更新命名空间导入(全量替换):** + +```bash +cr edit imports app.main -j '[["app.lib", ":as", "lib"], ["app.util", ":refer", ["helper"]]]' +``` + +--- + +## ⚠️ 常见陷阱和最佳实践 + +### 1. 路径索引动态变化问题 ⭐⭐⭐ + +**核心问题:** 删除或插入节点后,同级后续节点的索引会自动改变,导致之前查询到的路径立即失效。 + +**典型错误:** + +```bash +# 假设搜索到两个节点:[3,2,1,2] 和 [3,2,1,3] +cr tree delete app.main/fn -p "3,2,1,2" +# 此时原来的 [3,2,1,3] 已变成 [3,2,1,2] +cr tree delete app.main/fn -p "3,2,1,3" # ❌ 路径已失效,报错! +``` + +**解决方案:** + +✅ **从后往前操作** - 先操作索引大的节点: + +```bash +# 正确顺序:从后往前删除 +cr tree delete app.main/fn -p "3,2,1,3" +cr tree delete app.main/fn -p "3,2,1,2" +cr tree delete app.main/fn -p "3,2,1,1" +``` + +✅ **单次操作后立即重新搜索:** + +```bash +cr tree delete app.main/fn -p "3,2,1,2" +# 立即重新搜索获取更新后的路径 +cr query search "target" -f app.main/fn +cr tree delete app.main/fn -p "<新路径>" +``` + +✅ **批量操作前一次性规划:** + +```bash +# 1. 先收集所有需要修改的路径 +cr query search "old-pattern" -f app.main/fn > paths.txt +# 2. 手动排序后从后往前执行 +# 3. 或考虑用 cr edit def 整体重写定义 +``` + +⚠️ **避免:** 基于单次搜索结果连续修改多个节点。 + +### 2. 输入格式参数使用速查 ⭐⭐⭐ + +**参数混淆矩阵(已全面支持 `-e` 自动识别):** + +| 场景 | 示例用法 | 解析结果 | 说明 | +| ------------------- | -------------------------------------- | ----------------------------- | --------------------------------- | +| **表达式 (Cirru)** | `-e 'defn add (a b) (+ a b)'` | `["defn", "add", ...]` (List) | 默认按 Cirru one-liner 解析 | +| **原子符号 (Leaf)** | `--leaf -e 'my-symbol'` | `"my-symbol"` (Leaf) | **推荐**,避免被包装成 list | +| **字符串 (Leaf)** | `--leaf -e '\|hello world'` | `"hello world"` (Leaf) | 符号前缀 `\|` 表示字符串 | +| **JSON 数组** | `-e '["+", "x", "1"]'` | `["+", "x", "1"]` (List) | **自动识别** (含 `[` 且有 `"`) | +| **JSON 字符串** | `-e '"my leaf"'` | `"my leaf"` (Leaf) | **自动识别** (含引用的字符串) | +| **内联 JSON** | `-j '["defn", ...]'` | `["defn", ...]` (List) | 显式按 JSON 解析,忽略 Cirru 规则 | +| **外部文件** | `-f code.cirru` (或 `-f code.json -J`) | 根据文件内容解析 | `-J` 用于标记文件内是 JSON | + +**核心规则:** + +1. **智能识别模式**:`-e / --code` 现在会自动识别 JSON。如果你传入 `["a"]` 或 `"a"`,它会直接按 JSON 处理,无需再额外加 `-J` 或 `-j`。 +2. **强制 Leaf 模式**:如果你需要确保输入是一个叶子节点(符号或字符串),请在任何地方使用 `--leaf` 开关。它会将原始输入直接作为内容,不经过任何解析。 +3. **显式 JSON 模式**:如果你想明确告诉工具“这段就是 JSON”,优先用 `-j ''`。 +4. **统一性**:`cr tree` 和 `cr edit` 的所有子命令(replace, def, insert 等)现在共享完全相同的输入解析逻辑。 + +**实战示例:** + +```bash +# ✅ 替换表达式 +cr tree replace app.main/fn -p "2" -e 'println |hello' + +# ✅ 替换 leaf(推荐 --leaf) +cr tree replace app.main/fn -p "2,0" --leaf -e 'new-symbol' + +# ✅ 替换字符串 leaf +cr tree replace app.main/fn -p "2,1" --leaf -e '|new text' + +# ❌ 避免:用 -e 传单个 token(会变成 list) +cr tree replace app.main/fn -p "2,0" -e 'symbol' # 结果:["symbol"] +``` + +### 3. Cirru 字符串和数据类型 ⭐⭐ + +**Cirru 字符串前缀:** + +| Cirru 写法 | JSON 等价 | 使用场景 | +| -------------- | -------------- | ------------ | +| `\|hello` | `"hello"` | 推荐,简洁 | +| `"hello"` | `"hello"` | 也可以 | +| `\|a b c` | `"a b c"` | 包含空格 | +| `\|[tag] text` | `"[tag] text"` | 包含特殊字符 | + +**Tuple vs Vector:** + +```cirru +; ✅ Tuple - 用于事件、模式匹配 +:: :clipboard/read text + +; ✅ Vector - 用于 DOM 列表 +[] (button) (div) + +; ❌ 错误:用 vector 传事件 +send-to-component! $ [] :clipboard/read text +; 报错:tag-match expected tuple + +; ✅ 正确:用 tuple +send-to-component! $ :: :clipboard/read text +``` + +**记忆规则:** + +- **`::` (tuple)**: 事件、模式匹配、不可变数据结构 +- **`[]` (vector)**: DOM 元素列表、动态集合 + +### 4. 搜索结果路径的正确使用 ⭐ + +**搜索输出格式:** + +```bash +cr query search ".show" -f app.comp.home/comp-box +# 输出:[3,2,1,3,1,3,2,1,2,0] in .show confirm-plugin d! (fn () ...) +# ^^^^^^^^^^^^^^^^^^^^^^ 这是 .show 节点本身的路径 +# 父级路径是 [3,2,1,3,1,3,2,1,2] +``` + +**使用建议:** + +- **查看节点**:用返回的完整路径 + ```bash + cr tree show app.comp.home/comp-box -p "3,2,1,3,1,3,2,1,2,0" + ``` +- **替换节点**:用父级路径(去掉最后一个索引) + ```bash + cr tree replace app.comp.home/comp-box -p "3,2,1,3,1,3,2,1,2" -e 'new-expr' + ``` +- **插入/删除**:根据操作类型选择路径 + + ```bash + # 删除节点本身:用完整路径 + cr tree delete app.comp.home/comp-box -p "3,2,1,3,1,3,2,1,2,0" + + # 在节点后插入:用完整路径 + cr tree insert-after app.comp.home/comp-box -p "3,2,1,3,1,3,2,1,2,0" -e 'new-item' + ``` + +### 5. 推荐工作流程 + +**精细代码修改流程:** + +```bash +# 【步骤 1】模糊搜索定位大致位置 +cr query search "target-keyword" -f namespace/definition -l + +# 【步骤 2】逐层确认精确路径(多次运行,逐步深入) +cr tree show namespace/definition -p "" -d 1 +cr tree show namespace/definition -p "3" -d 2 +cr tree show namespace/definition -p "3,2,1" -d 2 + +# 【步骤 3】执行修改(单次操作) +cr tree replace namespace/definition -p "3,2,1" -e 'new-code' + +# 【步骤 4】验证结果 +cr tree show namespace/definition -p "3,2,1" +cr --check-only # 语法检查 +``` + +**批量修改策略:** + +```bash +# 方案 A:从后往前操作 +# 1. 收集所有路径并排序 +cr query search "pattern" -f app.main/fn +# 记录:[2,1,3], [2,1,2], [2,1,1] + +# 2. 从大到小执行 +cr tree delete app.main/fn -p "2,1,3" +cr tree delete app.main/fn -p "2,1,2" +cr tree delete app.main/fn -p "2,1,1" + +# 方案 B:整体重写 +cr query def app.main/fn > fn.json +# 编辑 fn.json +cr edit def app.main/fn -f fn.json -J +``` + +--- + +## 常见错误排查 + +| 错误信息 | 原因 | 解决方法 | +| ----------------------------------- | ----------------------------------- | ----------------------------------------------- | +| `Path index X out of bounds` | 路径已过期,节点被删除或索引改变 | 重新运行 `cr query search` 获取最新路径 | +| `tag-match expected tuple, got ...` | 传入 vector `[]` 而非 tuple `::` | 改用 `::` 构造数据结构 | +| 字符串被拆分成多个 token | Cirru 字符串没有用 `\|` 或 `"` 包裹 | 使用 `\|complete string` 或 `"complete string"` | +| Cirru one-liner 变成 list | `-e` 单个 token 会被包装成 list | 用 `-j '"token"'` 传入 leaf 节点 | +| `unexpected format in expression` | JSON 格式错误或 Cirru 语法错误 | 用 `cr cirru parse ''` 验证语法 | + +**调试技巧:** + +```bash +# 1. 验证 Cirru 语法 +cr cirru parse 'fn (x) (+ x 1)' + +# 2. 验证 JSON 格式 +echo '["fn", ["x"], ["+", "x", "1"]]' | jq . + +# 3. 检查整体语法 +cr --check-only + +# 4. 查看错误堆栈 +cr query error +``` diff --git a/llms/Respo.md b/llms/Respo.md new file mode 100644 index 0000000..17e1c7c --- /dev/null +++ b/llms/Respo.md @@ -0,0 +1,920 @@ +# Respo Development Guide for LLM Agents + +**🤖 This guide is specifically designed for LLM agents to develop, debug, and maintain Respo applications.** + +📚 **Related Documentation**: + +- [← Back to README](../README.md) +- [Beginner Guide](./beginner-guide.md) +- [CLI Tools Reference](../Agents.md) +- [API Reference](./api.md) + +--- + +## Project Structure + +The Respo project is a virtual DOM library written in Calcit-js, containing: + +- **Main codebase**: `compact.cirru` (2314 lines) - serialized source code +- **Compiled source**: `calcit.cirru` (13806 lines) - full AST representation +- **Namespaces**: 33 total namespaces organized by functionality +- **Version**: 0.16.21 +- **Dependencies**: memof (memoization), lilac (UI utilities), calcit-test (testing) + +### Core Namespace Organization + +**User-facing APIs** (what you typically use): + +- `respo.core` - Core APIs: defcomp, div, render!, clear-cache!, etc. +- `respo.comp.space` - Utility component comp-space (=<) +- `respo.comp.inspect` - Debugging component comp-inspect +- `respo.render.html` - HTML generation: make-string, make-html + +**Application layer** (in example app): + +- `respo.app.core` - Main application logic (\*store, dispatch!, render-app!) +- `respo.app.schema` - Data structures and schemas +- `respo.app.updater` - State management and updates +- `respo.app.comp.*` - Application components (container, task, todolist, wrap, zero) +- `respo.app.style.widget` - Application styles + +**Rendering and internal** (low-level): + +- `respo.render.diff` - Find differences between virtual DOM trees +- `respo.render.dom` - DOM element creation and manipulation +- `respo.render.effect` - Component lifecycle effects +- `respo.render.patch` - Apply DOM patches +- `respo.controller.client` - Client-side state management (activate-instance!, patch-instance!) +- `respo.controller.resolve` - Event handling and resolution + +**Utilities**: + +- `respo.util.dom` - DOM utilities +- `respo.util.format` - Element formatting (purify-element, mute-element) +- `respo.util.list` - List utilities (map-val, map-with-idx) +- `respo.util.detect` - Type detection (component?, element?, effect?) +- `respo.css` - CSS utilities +- `respo.cursor` - Cursor management for nested states + +--- + +## Essential Calcit CLI Commands for Development + +### 1. Exploration and Discovery + +```bash +# List all namespaces in the project +cr query ns + +# Get details about a specific namespace (imports, definitions) +cr query ns respo.core +cr query ns respo.app.core + +# List all definitions in a namespace +cr query defs respo.core +cr query defs respo.app.updater + +# Quick peek at a definition (signature, parameters, docs) +cr query peek respo.core/defcomp +cr query peek respo.core/render! + +# Get complete definition as JSON syntax tree +cr query def respo.core/render! +cr query def respo.app.core/dispatch! + +# Search for a symbol across all namespaces +cr query find render! +cr query find *store + +# Find all usages of a specific definition +cr query usages respo.core/render! +cr query usages respo.app.core/dispatch! +``` + +### 2. Precise Code Navigation (tree pattern) + +When you need to understand or modify specific parts of a definition: + +```bash +# Step 1: Read the complete definition first +cr query def respo.app.updater/updater + +# Step 2: Use tree show to examine the structure (limit depth to reduce output) +cr tree show respo.app.updater/updater -p "" -d 1 # View root level + +# Step 3: Dive deeper into specific indices +cr tree show respo.app.updater/updater -p "2" -d 1 # Check 3rd element +cr tree show respo.app.updater/updater -p "2,1" -d 1 # Check 2nd child of 3rd element + +# Step 4: Confirm target location before editing +cr tree show respo.app.updater/updater -p "2,1,0" # Final confirmation + +# Step 5: Use tree commands for surgical modifications +# JSON inline (recommended) +cr tree replace respo.app.updater/updater -p "2,1,0" -j '"new-value"' +# Or from stdin +echo '"new-value"' | cr tree replace respo.app.updater/updater -p "2,1,0" -s -J +``` + +echo '["defn", "hello", [], ["println", "|Hello"]]' | cr edit def respo.app.core/hello -s -J + +### 3. Code Modification (Agent Optimized) + +**Best Practice: Use JSON AST** +For LLM Agents, **JSON inline (`-j`) is the most reliable method** for code generation. It avoids whitespace/indentation ambiguity inherent in Cirru. + +**Input Modes:** + +- `-j ''`: **Recommended.** Inline JSON string. Escape quotes carefully. +- `-e ''`: Inline Cirru one-liner. Good for short, simple expressions. +- `-f ` / `-s`: Read from file/stdin (defaults to Cirru). +- `-J`: Combine with `-f`/`-s` to indicate JSON input. + +**JSON AST Structure Guide:** + +- Function: `(defn f (x) x)` -> `["defn", "f", ["x"], "x"]` +- Map: `{:a 1}` -> `["{}", [":a", "1"]]` +- String: `"|hello"` -> `"|hello"` (in JSON string: `"\"|hello\""`) +- Keyword: `:key` -> `":key"` + +**Common Commands:** + +```bash +# 1. Add/Update Definition (JSON) +# (defn greet (name) (println "|Hello" name)) +cr edit def respo.demo/greet -j '["defn", "greet", ["name"], ["println", "\"|Hello\"", "name"]]' + +# 2. Add Definition (Cirru One-liner - risky for complex code) +cr edit def respo.demo/simple -e 'defn simple (x) (+ x 1)' + +# 3. Update Imports (JSON) +# (ns respo.demo (:require [respo.core :refer [div span]])) +cr edit imports respo.demo -j '[["respo.core", ":refer", ["div", "span"]]]' + +# 4. Remove Definition +cr edit rm-def respo.demo/old-fn + +# 5. Namespace Operations +cr edit add-ns respo.new-feature +cr edit rm-ns respo.deprecated +``` + +**💡 Pro Tip: Validation** +If unsure about the JSON structure, generate it from Cirru first: + +```bash +cr cirru parse -O 'defn f (x) (+ x 1)' +# Output: ["defn", "f", ["x"], ["+", "x", "1"]] +``` + +### 4. Project Configuration + +```bash +# Get project configuration (init-fn, reload-fn, version) +cr query config + +# Set project configuration +cr edit config version "0.16.22" +cr edit config init-fn "respo.main/main!" +cr edit config reload-fn "respo.main/reload!" +``` + +### 5. Workflow: Building From Scratch + +Follow this sequence to create a new feature cleanly: + +**Step 1: Create Namespace** + +```bash +cr edit add-ns respo.app.feature-x +``` + +**Step 2: Add Imports** +Define dependencies (e.g., `respo.core`). + +```bash +# Cirru: (:require [respo.core :refer [defcomp div span]]) +cr edit imports respo.app.feature-x -j '[["respo.core", ":refer", ["defcomp", "div", "span"]]]' +``` + +**Step 3: Create Component** +Define the component logic. + +```bash +# Cirru: (defcomp comp-x (data) (div {} (<> "Feature X"))) +cr edit def respo.app.feature-x/comp-x -j '["defcomp", "comp-x", ["data"], ["div", ["{}"], ["<>", "\"|Feature X\""]]]' +``` + +**Step 4: Verify** + +```bash +cr query def respo.app.feature-x/comp-x +cr --check-only +``` + +**Step 5: Integrate** +Mount or use it in `respo.app.comp.container`. + +```bash +# 1. Add import to container ns +cr edit require respo.app.comp.container respo.app.feature-x + +# 2. Add usage (using surgical edit) +# Find where to insert using `cr tree show ...` +# cr tree insert-child ... -j '["respo.app.feature-x/comp-x", "data"]' +``` + +### 6. Documentation and Language + +```bash +# Check for syntax errors and warnings +cr --check-only +cr js --check-only + +# Get language documentation +cr docs api render! +cr docs ref component +cr docs list-api # List all API docs +cr docs list-guide # List all guide docs + +# Parse Cirru code to JSON (for understanding syntax) +cr cirru parse '(div {} (<> "hello"))' + +# Format JSON to Cirru code +cr cirru format '["div", {}, ["<>", "hello"]]' + +# Parse EDN to JSON +cr cirru parse-edn '{:a 1 :b [2 3]}' + +# Show Cirru syntax guide (read before generating Cirru) +cr cirru show-guide +``` + +### 6. Library Management + +```bash +# List official libraries +cr libs + +# Search libraries by keyword +cr libs search router + +# Read library README from GitHub +cr libs readme respo + +# Install/update dependencies +caps +``` + +### 7. Code Analysis + +```bash +# Call graph analysis from init-fn (or custom root) +cr analyze call-graph +cr analyze call-graph --root app.main/main! --ns-prefix app. --include-core --max-depth 5 --format json + +# Call count statistics +cr analyze count-calls +cr analyze count-calls --root app.main/main! --ns-prefix app. --include-core --format json --sort count +``` + +--- + +## Development Workflow for LLM Agents + +### Step 1: Understand the Problem + +```bash +# Always start by exploring related code +cr query ns respo.app.updater # Understand state management +cr query find my-function-name # Find where it's defined/used +cr query usages respo.core/render! # See how render! is used +``` + +### Step 2: Implement the Solution + +Use the **precise editing pattern** for complex changes: + +```bash +# 1. Read the whole definition +cr query def namespace/function-name + +# 2. Map out the structure with tree show +cr tree show namespace/function-name -p "" -d 1 + +# 3. Navigate to target position +cr tree show namespace/function-name -p "2,1" -d 1 + +# 4. Make the change (JSON inline recommended) +cr tree replace namespace/function-name -p "2,1,0" -j '["new", "code"]' + +# Or from stdin (JSON format) +echo '["new", "code"]' | cr tree replace namespace/function-name -p "2,1,0" -s -J + +# 5. Verify +cr tree show namespace/function-name -p "2,1" +``` + +### Step 3: Test and Validate + +```bash +# Check syntax without running +cr --check-only + +# Compile to JavaScript and check for errors +cr js --check-only + +# Run the app once to test +cr -1 + +# Compile to JavaScript once +cr -1 js + +# Watch mode (will call reload! on code changes) +cr +``` + +### Step 4: Debug Issues + +```bash +# Check for error messages +cr query error + +# Read error stack traces +cat .calcit-error.cirru # (if it exists) + +# Search for the problematic code +cr query find problem-symbol +cr query usages namespace/definition + +# Review the definition in detail +cr query def namespace/definition +``` + +--- + +## Common Patterns and Best Practices + +### 1. Component Definition Pattern + +**Cirru (Read):** + +```cirru +; Standard component structure +defcomp comp-name (param1 param2 & options) + div $ {} + :class-name "|component-name" + :style $ comp-style + <> "|Content" +``` + +**JSON AST (Write - for `cr edit`):** + +```json +[ + "defcomp", + "comp-name", + ["param1", "param2", "&", "options"], + [ + "div", + ["{}", [":class-name", "|component-name"], [":style", "comp-style"]], + ["<>", "|Content"] + ] +] +``` + +### 2. State Management Pattern + +```cirru +; Define store atom at app.core level +defatom *store $ {} + :states $ {} + :data $ {} + +; Create dispatcher +defn dispatch! (op) + reset! *store (updater @*store op) + +; Updater function pattern +defn updater (store op) + tag-match op + (:action-name value) $ + assoc store :data (process-action (:data store) value) + (:nested-action id op2) $ + update-in store [:data :nested id] (process-nested op2) + _ store +``` + +### 3. Rendering Pattern + +```cirru +; Initial render +defn render-app! () + render! mount-point (comp-container @*store) dispatch! + +; Watch for store changes +add-watch *store :changes $ fn () + render-app! + +; Hot reload with cache clearing +defn reload! () + remove-watch *store :changes + add-watch *store :changes $ fn () + render-app! + clear-cache! + render-app! +``` + +### 4. DOM Element Creation + +```cirru +; Using predefined elements (defn wrappers for create-element) +div $ {} (<> "text") +button $ {} (<> "Click me") +input $ {:value "|default"} +span $ {:class-name "|style-name"} (<> "content") + +; Dynamic elements with create-element +create-element :custom-tag $ {:prop-name "|value"} + <> "|child" + +; List rendering with list-> +list-> $ {} + :style $ {} (:display "|flex") + , $ {} + :a $ comp-item item-1 + :b $ comp-item item-2 + :c $ comp-item item-3 +``` + +### 5. Styling Pattern + +```cirru +; Define styles as maps +def style-container $ {} + :display "|flex" + :padding "|10px" + :background-color "|#f0f0f0" + +; Conditional styles +defn style-for-state (state) + if (= state :active) + assoc style-container :background-color "|#3388ff" + style-container + +; Merge styles +let + base $ {} (:color "|black") + extended $ merge base $ {} (:font-size 14) + extended +``` + +### 6. Event Handling + +```cirru +; Simple click handler +div + {} + :on-click $ fn (e dispatch!) + dispatch! [:button-clicked] + +; Input with value tracking +input + {} + :value "|current-value" + :on-input $ fn (e dispatch!) + let + value (e.target.value) + dispatch! [:input-changed value] + +; Keyboard events +div + {} + :on-keydown $ fn (e dispatch!) + when (= (e.key) "|Enter") + dispatch! [:submit-form] +``` + +--- + +## Debugging Common Issues + +### Issue: Component not re-rendering + +**Diagnosis**: + +```bash +# Check if render-app! is being called +cr query find render-app! +cr query usages respo.main/render-app! + +# Verify store watcher is set up +cr query def respo.app.core/dispatch! +cr query def respo.main/main! +``` + +**Solution Pattern**: + +```cirru +; Ensure watch is on *store +add-watch *store :changes $ fn () + render-app! + +; Ensure clear-cache! is called on reload +defn reload! () + remove-watch *store :changes + clear-cache! + add-watch *store :changes $ fn () + render-app! + render-app! +``` + +### Issue: State not updating + +**Diagnosis**: + +```bash +# Check updater function logic +cr query def respo.app.updater/updater + +# Verify dispatch! is calling updater correctly +cr query def respo.app.core/dispatch! + +# Check the state path in component +cr query def respo.app.comp.container/comp-container +``` + +**Solution Pattern**: + +```cirru +; Verify tag-match pattern matches dispatched action +tag-match op + (:action-name params) $ + ; Make sure return value is updated store + assoc store :data new-value + _ store ; Default case needed! + +; Ensure dispatch! is called with correct tuple +dispatch! [:action-name actual-value] +``` + +### Issue: Component effects not triggering + +**Diagnosis**: + +```bash +# Check effect definition +cr query def respo.core/defeffect # macro documentation + +# Find effect in component +cr query find my-effect +cr query usages respo.app.comp.task/my-effect +``` + +**Solution Pattern**: + +```cirru +; Effects must be first in component body +defcomp comp-with-effect (props) + [] + effect-name param1 param2 ; First! + div $ {} ; Then render + <> "|content" + +; Effect must match action lifecycle +defeffect my-effect (initial-value) + (action element at-place?) + when (= action :mount) + do (println "|mounted") + when (= action :update) + do (println "|updated") +``` + +### Issue: Hot reload breaking state + +**Diagnosis**: + +```bash +# Check reload! function +cr query def respo.main/reload! + +# Verify clear-cache! is called +cr query usages respo.core/clear-cache! +``` + +**Solution Pattern**: + +```cirru +; clear-cache! must be called during reload +defn reload! () + remove-watch *store :changes + clear-cache! ; Critical! + add-watch *store :changes $ fn () + render-app! + render-app! +``` + +--- + +## Modification Strategy: Safe Editing Guide + +### Before any edit, follow this checklist: + +1. **Understand the context** + + ```bash + cr query ns namespace-name # See imports and doc + cr query peek namespace-name/def-name # See signature + ``` + +2. **Map the exact location** + + ```bash + cr tree show namespace-name/def-name -p "" -d 2 # Overview + cr tree show namespace-name/def-name -p "2" -d 2 # Check section + cr tree show namespace-name/def-name -p "2,1" -d 2 # Precise location + ``` + +3. **Make surgical change** + +```bash +# JSON inline (recommended) +cr tree replace namespace-name/def-name -p "2,1,0" -j '"new-value"' + +# Or from stdin (JSON format) +echo '"new-value"' | cr tree replace namespace-name/def-name -p "2,1,0" -s -J +``` + +4. **Verify immediately** + ```bash + cr tree show namespace-name/def-name -p "2,1" # Confirm change + cr --check-only # Verify syntax + ``` + +### Common edit operations: + +```bash +# Replace a value (JSON inline) +cr tree replace ns/def -p "2,1,0" -j '"new-value"' + +# Insert before a position (JSON) +cr tree insert-before ns/def -p "2,1" -j '["new", "element"]' + +# Insert after a position (JSON) +cr tree insert-after ns/def -p "2,1" -j '["new", "element"]' + +# Delete a node +cr tree delete ns/def -p "2,1,0" + +# Insert as child (first child) +cr tree insert-child ns/def -p "2,1" -j '"child-value"' + +# Append as child (last child, from stdin) +echo '"child-value"' | cr tree append-child ns/def -p "2,1" -s -J +``` + +--- + +## Testing and Validation + +### Basic validation + +```bash +# Syntax check only (no execution) +cr --check-only + +# Check JavaScript compilation +cr js --check-only + +# Run application once +cr -1 + +# Compile to JS once +cr -1 js +``` + +### Test-driven development + +```bash +# Look at test files +cr query defs respo.test.main +cr query def respo.test.main/test-fn + +# Run tests +cr -1 ; (if init-fn runs tests) +``` + +### Error diagnosis + +```bash +# View error file +cr query error +cat .calcit-error.cirru + +# Search for the problematic definition +cr query find problem-name + +# Check the full definition +cr query def namespace/problem-name + +# Validate dependencies +cr query ns namespace-name # Check imports +``` + +--- + +## Important Notes for LLM Agents + +### ⚠️ Critical Rules + +1. **NEVER directly edit `calcit.cirru` or `compact.cirru`** with text editors + + - Use `cr edit` commands instead + - These are serialized AST structures, not human-readable code + +2. **ALWAYS use relative paths for documentation links** + + - Use `../` and `../../` for navigation + - This allows easy file discovery for LLM tools + +3. **ALWAYS check syntax before assuming it's correct** + + ```bash + cr --check-only + ``` + +4. **ALWAYS verify modifications work** + + ```bash + cr tree show namespace/def -p "modified-path" # Confirm change + cr --check-only # Check syntax + cr -1 # Test run + ``` + +5. **Use peek before def** to reduce token consumption + ```bash + cr query peek ns/def # Light summary + cr query def ns/def # Full AST (use only if needed) + ``` + +### 🎯 Optimization Tips for Token Usage + +```bash +# Fast exploration with limited output +cr query peek respo.core/defcomp # 5-10 lines +cr query defs respo.app.updater # Quick list + +# Slower but comprehensive +cr query def respo.app.updater/updater # Full JSON AST + +# Use -d flag to limit JSON depth +cr tree show ns/def -p "2,1" -d 1 # Shallow +cr tree show ns/def -p "2,1" -d 3 # Medium +cr tree show ns/def -p "2,1" # Full (default) + +# Search before diving deep +cr query find my-function # Find location first +cr query usages ns/def # See usage patterns +``` + +### 📖 Documentation Strategy + +When stuck, use these resources in order: + +1. This file (Respo-Agent.md) - you are here +2. [README.md](../README.md) - Project overview and index +3. [Beginner Guide](./beginner-guide.md) - Conceptual introduction +4. [API Reference](./api.md) - Specific API documentation +5. [Guide docs](./guide/) - Detailed topics +6. `cr docs api ` - Language documentation +7. Project code itself: `cr query ns ` + +--- + +## Quick Reference + +### Most Used Commands + +```bash +# Exploration (read-only, no changes) +cr query ns # List namespaces +cr query ns respo.core # Read namespace details +cr query defs respo.app.core # List definitions +cr query peek respo.core/render! # Quick peek +cr query def respo.core/render! # Full definition +cr query find render! # Search globally +cr query usages respo.core/render! # Find usages + +# Navigation (precise editing) +cr tree show ns/def -p "" -d 1 # View structure +cr tree show ns/def -p "2,1" -d 1 # Drill down +cr tree show ns/def -p "2,1,0" # Confirm target + +# Modification (careful!) +cr edit def ns/def -j '["defn", "func", [], "body"]' +cr tree replace ns/def -p "2,1,0" -j '"value"' +cr edit rm-def ns/def + +# Validation +cr --check-only # Check syntax +cr query error # View errors +cr -1 # Test run +``` + +### File Paths in Documentation + +When referring to files from within `docs/`: + +- `./` - same directory +- `../` - parent (docs/ to root) +- `../../` - grandparent (docs/apis/ to root) + +Example from `docs/apis/defcomp.md`: + +```markdown +- [Back to README](../../README.md) +- [API Overview](../api.md) +- [Another API](./render!.md) +``` + +--- + +This guide evolves as the project grows. Last updated: 2025-12-22 + +# Calcit & Respo 开发避坑指南 + +本文档总结了在 Calcit 和 Respo 开发过程中遇到的常见问题和最佳实践。 + +## 1. 字符串语法 (String Syntax) + +Calcit 使用 `|` 前缀来表示字符串字面量。 + +- **正确**: `|Hello` (编译为 `"Hello"`) +- **正确**: `"Hello"` (标准 Cirru 字符串,编译为 `"Hello"`) +- **错误**: `Hello` (会被解析为 Symbol) + +**CLI 操作注意**: +在使用 `cr tree replace` 修改字符串时,推荐使用 `|` 前缀或 `--json-leaf` 确保类型正确。 + +```bash +# 推荐:使用 | 前缀 +cr tree replace ... -e "|Get Started" + +# 推荐:使用 --json-leaf 明确指定为叶子节点 +cr tree replace ... -e '"Get Started"' --json-leaf +``` + +如果直接使用 `-e '"Get Started"'` 且不加 `--json-leaf`,可能会被解析为包含引号的字符串 `"\"Get Started\""`,导致显示多余引号。 + +## 2. Respo 文本渲染 (Text Rendering) + +Respo 的 HTML 标签(如 `div`, `span`, `button`)**不能直接接受原始字符串作为子节点**。 + +**错误写法** (会导致 `Invalid data in elements tree`): + +```cirru +div ({}) + |SomeText +``` + +**正确写法 1: 使用 `:inner-text` 属性** (推荐用于纯文本标签) + +```cirru +div $ {} (:inner-text |SomeText) +``` + +**正确写法 2: 使用 `<>` 组件** (推荐用于混合内容) + +```cirru +div ({}) + <> |SomeText + span $ {} (:inner-text |Other) +``` + +## 3. 样式定义 (Styles) + +在 `defstyle` 中定义样式时: + +- **数值属性**: 像 `font-weight` 这样的属性,如果使用数字(如 `700`),确保它是数字类型而不是字符串。 + - 错误: `(:font-weight |bold)` (如果库不支持) + - 正确: `(:font-weight 700)` + +## 4. CLI 调试技巧 + +- **检查代码**: `cr js --check-only` + + - 这是一个非常快速的检查命令,能发现未定义的变量 (Warnings) 和语法错误,而不会生成 JS 文件。 + - **务必关注 Warnings**: 很多运行时错误(如 `unknown head`)都是因为使用了未定义的 Symbol(可能是忘记加 `|` 前缀的字符串)。 + +- **查看节点结构**: `cr tree show -p ` + + - 在修改前,先查看目标节点的结构(是 `list` 还是 `leaf`),确认路径是否正确。 + +- **精确修改**: `cr tree replace` + - 配合 `-p` 路径参数进行精确修改,避免破坏周围结构。 + +## 5. 命名空间 (Namespaces) + +- **修改 Imports**: 使用 `cr edit imports -j '...'` + - 这是修改 `:require` 最安全的方式。 + - 如果遇到 `invalid ns form` 错误,通常是因为 `ns` 定义格式被破坏,可以尝试清空 imports 再重新添加。 diff --git a/package.json b/package.json index 0f54b2b..50c4d11 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@calcit/editor", - "version": "0.9.11", + "version": "0.9.12", "description": "Structural Editor for Calcit Language", "bin": { "ct": "server.mjs"