@mulmoclaude/core 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/helps/billing-clients-worklog.md +15 -12
- package/assets/helps/collection-skills.md +67 -22
- package/assets/helps/todo-collection.md +13 -7
- package/dist/{deriveAll-BQ_p5HSB.cjs → calendarGrid-CaR-q36Y.js} +352 -74
- package/dist/calendarGrid-CaR-q36Y.js.map +1 -0
- package/dist/calendarGrid-gN4sei7s.cjs +1070 -0
- package/dist/calendarGrid-gN4sei7s.cjs.map +1 -0
- package/dist/collection/core/backlinks.d.ts +20 -0
- package/dist/collection/core/ids.d.ts +12 -0
- package/dist/collection/core/recordZ.d.ts +23 -0
- package/dist/collection/core/schema.d.ts +72 -418
- package/dist/collection/core/schemaZ.d.ts +878 -0
- package/dist/collection/core/templatePath.d.ts +34 -0
- package/dist/collection/core/uiTypes.d.ts +22 -0
- package/dist/collection/core/where.d.ts +9 -15
- package/dist/collection/index.cjs +50 -330
- package/dist/collection/index.cjs.map +1 -1
- package/dist/collection/index.d.ts +2 -0
- package/dist/collection/index.js +14 -301
- package/dist/collection/index.js.map +1 -1
- package/dist/collection/paths.cjs +1 -1
- package/dist/collection/paths.cjs.map +1 -1
- package/dist/collection/paths.js +1 -1
- package/dist/collection/paths.js.map +1 -1
- package/dist/collection/registry/server/index.cjs +1 -1
- package/dist/collection/registry/server/index.js +1 -1
- package/dist/collection/server/discovery.d.ts +2 -244
- package/dist/collection/server/index.cjs +8 -2
- package/dist/collection/server/index.d.ts +1 -0
- package/dist/collection/server/index.js +3 -2
- package/dist/collection/server/io.d.ts +9 -0
- package/dist/collection/server/ontology.d.ts +35 -0
- package/dist/collection/server/templatePath.d.ts +1 -34
- package/dist/collection/server/validate.d.ts +11 -7
- package/dist/collection-watchers/index.cjs +5 -4
- package/dist/collection-watchers/index.cjs.map +1 -1
- package/dist/collection-watchers/index.js +3 -2
- package/dist/collection-watchers/index.js.map +1 -1
- package/dist/feeds/index.cjs +4 -4
- package/dist/feeds/index.js +2 -2
- package/dist/feeds/ingestTypes.d.ts +15 -40
- package/dist/feeds/server/index.cjs +5 -5
- package/dist/feeds/server/index.js +3 -3
- package/dist/{ingestTypes-C7EheYZX.cjs → ingestTypes-DG8RExWt.cjs} +3 -3
- package/dist/ingestTypes-DG8RExWt.cjs.map +1 -0
- package/dist/{ingestTypes-CmJeOUJc.js → ingestTypes-V4upKeBx.js} +2 -2
- package/dist/ingestTypes-V4upKeBx.js.map +1 -0
- package/dist/{server-DKXXFTbw.js → server-BNTR7ySK.js} +531 -196
- package/dist/server-BNTR7ySK.js.map +1 -0
- package/dist/{server-G6GtOdaW.cjs → server-bsNrf2yW.cjs} +572 -213
- package/dist/server-bsNrf2yW.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/deriveAll-BQ_p5HSB.cjs.map +0 -1
- package/dist/deriveAll-CMFXDQ_G.js +0 -506
- package/dist/deriveAll-CMFXDQ_G.js.map +0 -1
- package/dist/ingestTypes-C7EheYZX.cjs.map +0 -1
- package/dist/ingestTypes-CmJeOUJc.js.map +0 -1
- package/dist/server-DKXXFTbw.js.map +0 -1
- package/dist/server-G6GtOdaW.cjs.map +0 -1
|
@@ -103,18 +103,19 @@ Don't push for fields the user hasn't given you — leave optional fields out of
|
|
|
103
103
|
the JSON entirely.
|
|
104
104
|
|
|
105
105
|
## What to do
|
|
106
|
-
**Add**: derive an `id`, build the record,
|
|
107
|
-
|
|
108
|
-
silently
|
|
106
|
+
**Add**: derive an `id`, build the record, write via `manageCollection`
|
|
107
|
+
`putItems` with `mode: "create"` — an id collision comes back as a `rejected`
|
|
108
|
+
row instead of silently overwriting; pick a fresh slug and retry.
|
|
109
109
|
|
|
110
|
-
**List / look up**:
|
|
110
|
+
**List / look up**: `manageCollection` `getItems`, answer from the rows. Don't
|
|
111
111
|
recite the whole table in chat — the user sees it at `/collections/clients`. A
|
|
112
112
|
one-line confirmation ("Added Acme Corp.") is enough.
|
|
113
113
|
|
|
114
|
-
**Update**:
|
|
115
|
-
|
|
114
|
+
**Update**: `putItems` with `mode: "merge"` and a partial row
|
|
115
|
+
(`{ id, <changed fields> }`) — the default upsert replaces the whole record.
|
|
116
116
|
|
|
117
|
-
**Delete**: confirm once if the request is ambiguous, then remove the file
|
|
117
|
+
**Delete**: confirm once if the request is ambiguous, then remove the file
|
|
118
|
+
(`manageCollection` has no delete).
|
|
118
119
|
|
|
119
120
|
## Linking to a client in chat
|
|
120
121
|
Link to the collection view, not the raw JSON path:
|
|
@@ -179,18 +180,20 @@ description: A simple timesheet — log billable / non-billable hours per client
|
|
|
179
180
|
## clientId resolution
|
|
180
181
|
`clientId` is a `ref` to the `clients` collection — write the raw client slug.
|
|
181
182
|
When the user says "log 2 hours for Acme":
|
|
182
|
-
-
|
|
183
|
-
(case-insensitive substring is
|
|
183
|
+
- `manageCollection` `getItems` on `clients` (`fields: ["id", "name"]`) and
|
|
184
|
+
find the slug whose `name` matches "Acme" (case-insensitive substring is
|
|
185
|
+
fine).
|
|
184
186
|
- If no match: ask whether to create the client first (via the `clients` skill)
|
|
185
187
|
or use a literal slug they supply. Never invent a clientId that doesn't exist —
|
|
186
188
|
it renders as a broken link.
|
|
187
189
|
|
|
188
190
|
## What to do
|
|
189
191
|
**Log hours**: derive `id`, default `billable: true`, default `date` to today if
|
|
190
|
-
unspecified,
|
|
191
|
-
|
|
192
|
+
unspecified, write via `manageCollection` `putItems` (`mode: "create"`; fix any
|
|
193
|
+
`rejected` row from its `problem` text and retry). (This skill tracks total
|
|
194
|
+
hours per day per client — not start/end times.)
|
|
192
195
|
|
|
193
|
-
**List / summarize**:
|
|
196
|
+
**List / summarize**: `manageCollection` `getItems`, answer from the rows. Don't
|
|
194
197
|
recite the table — point at `/collections/worklog`. For aggregates ("how many
|
|
195
198
|
hours did I bill Acme last month?") group by clientId + date range and answer in
|
|
196
199
|
one line.
|
|
@@ -31,7 +31,7 @@ data/skills/<slug>/ ← YOU write here (Write / Edit)
|
|
|
31
31
|
SKILL.md · schema.json · templates/*.md
|
|
32
32
|
|
|
33
33
|
data/<name>/items/ ← the records (separate from the skill dir)
|
|
34
|
-
<id>.json ← one record per file (
|
|
34
|
+
<id>.json ← one record per file (write via manageCollection putItems)
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
- **Author under `data/skills/<slug>/`, NEVER `.claude/skills/<slug>/`
|
|
@@ -148,7 +148,7 @@ skipped, never crashes the host):
|
|
|
148
148
|
|
|
149
149
|
`string` · `text` (multi-line) · `email` · `number` · `date` (`YYYY-MM-DD`) ·
|
|
150
150
|
`datetime` (`YYYY-MM-DDTHH:MM`) · `boolean` · `markdown` · `money` · `enum` ·
|
|
151
|
-
`ref` · `embed` · `table` · `derived` · `image` · `file` · `toggle`
|
|
151
|
+
`ref` · `embed` · `backlinks` · `table` · `derived` · `image` · `file` · `toggle`
|
|
152
152
|
|
|
153
153
|
Every field spec needs a `type` and a `label`. Extra keys by type:
|
|
154
154
|
|
|
@@ -179,9 +179,23 @@ Every field spec needs a `type` and a `label`. Extra keys by type:
|
|
|
179
179
|
sibling as a dropdown picker in the editor and hides its own cell (the embed
|
|
180
180
|
owns it). E.g. a multi-issuer invoice: a `ref` field `issuerId → profile`
|
|
181
181
|
plus `{ "type": "embed", "to": "profile", "idField": "issuerId" }`.
|
|
182
|
+
- **`backlinks`** — `from: "<source-slug>"`, `via: "<ref-field-in-source>"`,
|
|
183
|
+
`display: ["<source-col>", ...]`, optional `filter: { "field": ..., "in": [...] }`.
|
|
184
|
+
The **reverse** side of a `ref`: a read-only sub-table (detail view only) of
|
|
185
|
+
the records in `from` whose `via` ref points at this record — each row links
|
|
186
|
+
to that record. **Nothing is stored** on this record (like `derived`/`embed`);
|
|
187
|
+
you never write it, and the rows update whenever the source records change.
|
|
188
|
+
`display` names the source columns to show — a derived source column works
|
|
189
|
+
when its formula is self-contained (e.g. an invoice `total` summing its own
|
|
190
|
+
line items), but one that derefs yet another collection renders em-dash;
|
|
191
|
+
`filter` narrows rows by a source field's value, same shape as `when`. E.g. a
|
|
192
|
+
client's open invoices:
|
|
193
|
+
`{ "type": "backlinks", "label": "Invoices", "from": "invoice", "via": "clientId", "display": ["issueDate", "total", "status"], "filter": { "field": "status", "in": ["draft", "sent"] } }`.
|
|
194
|
+
Resolution is fail-soft: an unknown `from` / `via` / `display` column just
|
|
195
|
+
renders an empty sub-table — no error, so author the `ref` side first.
|
|
182
196
|
- **`table`** — `of: { <col>: <sub-field-spec>, ... }`. An array of rows. Each
|
|
183
|
-
sub-field is a flat spec; sub-fields **cannot** be `table` or
|
|
184
|
-
(no nested tables, no computed columns).
|
|
197
|
+
sub-field is a flat spec; sub-fields **cannot** be `table`, `derived`, or
|
|
198
|
+
`backlinks` (no nested tables, no computed columns).
|
|
185
199
|
- **`derived`** — `formula: "<expr>"`, optional `display` (`number` default, or
|
|
186
200
|
`money` / `string` / `date`) and `currency`. **Read-only, host-computed** —
|
|
187
201
|
you NEVER write derived values into the JSON; the host recomputes them on
|
|
@@ -761,10 +775,32 @@ single source of truth and the "done" checkbox is a `toggle` field projecting it
|
|
|
761
775
|
|
|
762
776
|
## Records — one JSON object per file
|
|
763
777
|
|
|
764
|
-
|
|
765
|
-
|
|
778
|
+
Each record is a plain file at `<dataPath>/<id>.json` (the `id` field's value is
|
|
779
|
+
the filename, no extension) — that is the storage model. But you read and write
|
|
780
|
+
records through **`manageCollection`**, not raw file I/O:
|
|
781
|
+
|
|
782
|
+
- **Create / update — `putItems`.** Every row is validated against the schema
|
|
783
|
+
BEFORE the write (required fields, enum membership, primaryKey = record id)
|
|
784
|
+
and the result reports `{ written, rejected }` — fix each rejected row from
|
|
785
|
+
its `problem` text and retry just those rows. Use `mode: "create"` when
|
|
786
|
+
adding, so an id collision is rejected instead of silently overwritten, and
|
|
787
|
+
`mode: "merge"` with a partial row (`{ id, <changed fields> }`) when
|
|
788
|
+
updating — the default upsert replaces the WHOLE record and would erase
|
|
789
|
+
every optional field the row omits.
|
|
790
|
+
- **Read / list — `getItems`.** The only way to see host-computed `derived` /
|
|
791
|
+
`toggle` / `embed` values (the stored JSON never contains them). Pass `ids`
|
|
792
|
+
/ `fields` on large collections to keep the result small — e.g.
|
|
793
|
+
`fields: ["id"]` to check for an id collision before an add.
|
|
794
|
+
- **Delete** — remove the record file (`manageCollection` has no delete).
|
|
795
|
+
- **Cross-collection questions — `getOntology`.** Returns every collection in
|
|
796
|
+
the workspace with its `primaryKey`, effective `displayField`, record count,
|
|
797
|
+
and outbound `ref` / `embed` relations (field → target slug, including refs
|
|
798
|
+
inside `table` columns as `lines.clientId`). When a question spans
|
|
799
|
+
collections ("which clients have unpaid invoices AND unlogged hours?"),
|
|
800
|
+
call it first to see which collections exist and how they join, then
|
|
801
|
+
`getItems` only the ones involved — instead of reading every schema.json.
|
|
766
802
|
- **Id charset** (enforced by `safeRecordId` in
|
|
767
|
-
`packages/
|
|
803
|
+
`packages/core/src/collection/server/paths.ts` — the single source of
|
|
768
804
|
truth; `manageCollection` rejects ids that fail it): start and end with a
|
|
769
805
|
letter or digit; inside, also `-`, `_`, and `.` are allowed (so natural keys
|
|
770
806
|
like a Slack ts `1718900000.123456` or a SemVer `1.2.3` work). **No** path
|
|
@@ -774,6 +810,25 @@ single source of truth and the "done" checkbox is a `toggle` field projecting it
|
|
|
774
810
|
enforces this on every targeted read/write, so an id that only _looks_ fine in
|
|
775
811
|
a full `getItems` listing but violates the rule can't be updated or deleted by
|
|
776
812
|
id — fix the id, don't work around it with raw file I/O.
|
|
813
|
+
- **Never write `derived` fields**, and never write an `embed` or `backlinks`
|
|
814
|
+
field — all are display-only / host-computed (`putItems` rejects rows that
|
|
815
|
+
carry them).
|
|
816
|
+
- Leave optional fields out of the row entirely rather than writing empty
|
|
817
|
+
strings.
|
|
818
|
+
- For a `ref` field, write the raw target slug, and make sure that record
|
|
819
|
+
actually exists in the target collection — an invalid slug renders as a broken
|
|
820
|
+
link. The host enforces structure and safety; **you own semantic correctness**
|
|
821
|
+
(valid refs, sane values).
|
|
822
|
+
|
|
823
|
+
### Raw file I/O on records — the escape hatch
|
|
824
|
+
|
|
825
|
+
Read / Write / Edit on the record files stays available (files are the source
|
|
826
|
+
of truth), but it skips `putItems`' pre-write validation — a mistake lands on
|
|
827
|
+
disk instead of coming back as a `rejected` row. Reach for it only when the
|
|
828
|
+
tool can't do the job: bulk file surgery, or repairing a file so malformed
|
|
829
|
+
that `manageCollection` can't address it. If you do write record files
|
|
830
|
+
directly:
|
|
831
|
+
|
|
777
832
|
- **The file MUST be valid JSON.** A malformed record is **silently skipped** at
|
|
778
833
|
read time (logged server-side, but invisible in the UI) — so one bad file out
|
|
779
834
|
of fifteen looks like "fourteen records vanished." The #1 cause is an
|
|
@@ -782,22 +837,12 @@ single source of truth and the "done" checkbox is a `toggle` field projecting it
|
|
|
782
837
|
(`text`, `markdown`, a long `objective`), either escape every inner ASCII quote
|
|
783
838
|
as `\"`, or — better — use the language's own quotation marks (`「」`/`『』` for
|
|
784
839
|
Japanese, `‘ ’`/`“ ”` or `'…'` for English) so no escaping is needed.
|
|
785
|
-
|
|
840
|
+
- `presentCollection` re-validates the records and reports any unreadable /
|
|
786
841
|
malformed / schema-violating files back to you (a `⚠️` in its result) — so
|
|
787
|
-
always follow a batch of writes with a `presentCollection` call and
|
|
788
|
-
any ⚠️ it returns
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
overwriting. Update = Read, merge, Write back (preserve fields you weren't
|
|
792
|
-
asked to change). Delete = remove the file.
|
|
793
|
-
- **Never write `derived` fields**, and never write an `embed` field — both are
|
|
794
|
-
display-only / host-computed.
|
|
795
|
-
- Leave optional fields out of the JSON entirely rather than writing empty
|
|
796
|
-
strings.
|
|
797
|
-
- For a `ref` field, write the raw target slug, and make sure that record
|
|
798
|
-
actually exists in the target collection — an invalid slug renders as a broken
|
|
799
|
-
link. The host enforces structure and safety; **you own semantic correctness**
|
|
800
|
-
(valid refs, sane values).
|
|
842
|
+
always follow a batch of direct writes with a `presentCollection` call and
|
|
843
|
+
**act on any ⚠️ it returns**, rather than assuming every record landed.
|
|
844
|
+
(This safety net applies after `putItems` batches too, but direct writes are
|
|
845
|
+
where it earns its keep.)
|
|
801
846
|
|
|
802
847
|
## End-to-end: creating a new collection skill
|
|
803
848
|
|
|
@@ -80,11 +80,16 @@ description: The user's personal todo list. Use whenever they add, list, edit,
|
|
|
80
80
|
- Do NOT write a `done` field — it's a projection of `status`.
|
|
81
81
|
|
|
82
82
|
## What to do
|
|
83
|
-
- **Add**: derive an id, default `status: "todo"`,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
- **
|
|
87
|
-
|
|
83
|
+
- **Add**: derive an id, default `status: "todo"`, write via `manageCollection`
|
|
84
|
+
`putItems` (`mode: "create"`; a rejected row comes back with a `problem` to
|
|
85
|
+
fix and retry).
|
|
86
|
+
- **List**: `manageCollection` `getItems` (the only way to see the computed
|
|
87
|
+
`done` toggle); point the user at `/collections/todos` rather than reciting
|
|
88
|
+
the table.
|
|
89
|
+
- **Mark done**: `putItems` with `mode: "merge"` and `{ id, status: "done" }`.
|
|
90
|
+
- **Edit**: `putItems` `mode: "merge"` with a partial row — the default upsert
|
|
91
|
+
replaces the whole record. **Delete**: remove the file (`manageCollection`
|
|
92
|
+
has no delete).
|
|
88
93
|
- After a change, call `presentCollection` with slug `todos` (and the id) to show
|
|
89
94
|
it inline.
|
|
90
95
|
```
|
|
@@ -131,8 +136,9 @@ single array) with the columns in the sibling `columns.json`. Convert them:
|
|
|
131
136
|
the legacy list had.
|
|
132
137
|
3. **Fold `completed` into `status`.** A legacy `completed: true` item belongs in
|
|
133
138
|
the done column; don't carry a separate boolean.
|
|
134
|
-
4. **One
|
|
135
|
-
preserving each legacy `id`
|
|
139
|
+
4. **One record per item.** Split the array into one `putItems` row per item
|
|
140
|
+
(stored as `data/todos/items/<id>.json`), preserving each legacy `id`
|
|
141
|
+
verbatim.
|
|
136
142
|
5. **Convert `createdAt`.** The legacy value is Unix milliseconds — convert it to
|
|
137
143
|
a `YYYY-MM-DD` `date` field. Carry `priority`, `dueDate`, `note` straight
|
|
138
144
|
across.
|