@mulmoclaude/core 0.13.0 → 0.14.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 +42 -19
- package/assets/helps/gemini.md +5 -3
- package/assets/helps/todo-collection.md +13 -7
- package/dist/{deriveAll-CMFXDQ_G.js → calendarGrid-C1rfCXJ3.cjs} +468 -3
- package/dist/calendarGrid-C1rfCXJ3.cjs.map +1 -0
- package/dist/{deriveAll-BQ_p5HSB.cjs → calendarGrid-kO6rGfm0.js} +289 -74
- package/dist/calendarGrid-kO6rGfm0.js.map +1 -0
- package/dist/collection/core/ids.d.ts +12 -0
- package/dist/collection/core/recordZ.d.ts +25 -0
- package/dist/collection/core/schema.d.ts +65 -418
- package/dist/collection/core/schemaZ.d.ts +846 -0
- package/dist/collection/core/templatePath.d.ts +34 -0
- package/dist/collection/core/where.d.ts +9 -15
- package/dist/collection/index.cjs +34 -295
- package/dist/collection/index.cjs.map +1 -1
- package/dist/collection/index.d.ts +1 -0
- package/dist/collection/index.js +5 -270
- 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 +3 -1
- package/dist/collection/server/index.js +2 -2
- package/dist/collection/server/templatePath.d.ts +1 -34
- package/dist/collection/server/validate.d.ts +11 -7
- package/dist/collection-watchers/clock.d.ts +1 -0
- package/dist/collection-watchers/index.cjs +13 -4
- package/dist/collection-watchers/index.cjs.map +1 -1
- package/dist/collection-watchers/index.d.ts +1 -0
- package/dist/collection-watchers/index.js +13 -5
- 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-BIFXlw7M.cjs} +3 -3
- package/dist/ingestTypes-BIFXlw7M.cjs.map +1 -0
- package/dist/{ingestTypes-CmJeOUJc.js → ingestTypes-KuYPX9Ea.js} +2 -2
- package/dist/ingestTypes-KuYPX9Ea.js.map +1 -0
- package/dist/{server-DKXXFTbw.js → server-Cc6XcyOA.js} +417 -187
- package/dist/server-Cc6XcyOA.js.map +1 -0
- package/dist/{server-G6GtOdaW.cjs → server-Cd5WdM8a.cjs} +440 -198
- package/dist/server-Cd5WdM8a.cjs.map +1 -0
- package/package.json +2 -2
- package/dist/deriveAll-BQ_p5HSB.cjs.map +0 -1
- 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>/`
|
|
@@ -761,10 +761,25 @@ single source of truth and the "done" checkbox is a `toggle` field projecting it
|
|
|
761
761
|
|
|
762
762
|
## Records — one JSON object per file
|
|
763
763
|
|
|
764
|
-
|
|
765
|
-
|
|
764
|
+
Each record is a plain file at `<dataPath>/<id>.json` (the `id` field's value is
|
|
765
|
+
the filename, no extension) — that is the storage model. But you read and write
|
|
766
|
+
records through **`manageCollection`**, not raw file I/O:
|
|
767
|
+
|
|
768
|
+
- **Create / update — `putItems`.** Every row is validated against the schema
|
|
769
|
+
BEFORE the write (required fields, enum membership, primaryKey = record id)
|
|
770
|
+
and the result reports `{ written, rejected }` — fix each rejected row from
|
|
771
|
+
its `problem` text and retry just those rows. Use `mode: "create"` when
|
|
772
|
+
adding, so an id collision is rejected instead of silently overwritten, and
|
|
773
|
+
`mode: "merge"` with a partial row (`{ id, <changed fields> }`) when
|
|
774
|
+
updating — the default upsert replaces the WHOLE record and would erase
|
|
775
|
+
every optional field the row omits.
|
|
776
|
+
- **Read / list — `getItems`.** The only way to see host-computed `derived` /
|
|
777
|
+
`toggle` / `embed` values (the stored JSON never contains them). Pass `ids`
|
|
778
|
+
/ `fields` on large collections to keep the result small — e.g.
|
|
779
|
+
`fields: ["id"]` to check for an id collision before an add.
|
|
780
|
+
- **Delete** — remove the record file (`manageCollection` has no delete).
|
|
766
781
|
- **Id charset** (enforced by `safeRecordId` in
|
|
767
|
-
`packages/
|
|
782
|
+
`packages/core/src/collection/server/paths.ts` — the single source of
|
|
768
783
|
truth; `manageCollection` rejects ids that fail it): start and end with a
|
|
769
784
|
letter or digit; inside, also `-`, `_`, and `.` are allowed (so natural keys
|
|
770
785
|
like a Slack ts `1718900000.123456` or a SemVer `1.2.3` work). **No** path
|
|
@@ -774,6 +789,24 @@ single source of truth and the "done" checkbox is a `toggle` field projecting it
|
|
|
774
789
|
enforces this on every targeted read/write, so an id that only _looks_ fine in
|
|
775
790
|
a full `getItems` listing but violates the rule can't be updated or deleted by
|
|
776
791
|
id — fix the id, don't work around it with raw file I/O.
|
|
792
|
+
- **Never write `derived` fields**, and never write an `embed` field — both are
|
|
793
|
+
display-only / host-computed (`putItems` rejects rows that carry them).
|
|
794
|
+
- Leave optional fields out of the row entirely rather than writing empty
|
|
795
|
+
strings.
|
|
796
|
+
- For a `ref` field, write the raw target slug, and make sure that record
|
|
797
|
+
actually exists in the target collection — an invalid slug renders as a broken
|
|
798
|
+
link. The host enforces structure and safety; **you own semantic correctness**
|
|
799
|
+
(valid refs, sane values).
|
|
800
|
+
|
|
801
|
+
### Raw file I/O on records — the escape hatch
|
|
802
|
+
|
|
803
|
+
Read / Write / Edit on the record files stays available (files are the source
|
|
804
|
+
of truth), but it skips `putItems`' pre-write validation — a mistake lands on
|
|
805
|
+
disk instead of coming back as a `rejected` row. Reach for it only when the
|
|
806
|
+
tool can't do the job: bulk file surgery, or repairing a file so malformed
|
|
807
|
+
that `manageCollection` can't address it. If you do write record files
|
|
808
|
+
directly:
|
|
809
|
+
|
|
777
810
|
- **The file MUST be valid JSON.** A malformed record is **silently skipped** at
|
|
778
811
|
read time (logged server-side, but invisible in the UI) — so one bad file out
|
|
779
812
|
of fifteen looks like "fourteen records vanished." The #1 cause is an
|
|
@@ -782,22 +815,12 @@ single source of truth and the "done" checkbox is a `toggle` field projecting it
|
|
|
782
815
|
(`text`, `markdown`, a long `objective`), either escape every inner ASCII quote
|
|
783
816
|
as `\"`, or — better — use the language's own quotation marks (`「」`/`『』` for
|
|
784
817
|
Japanese, `‘ ’`/`“ ”` or `'…'` for English) so no escaping is needed.
|
|
785
|
-
|
|
818
|
+
- `presentCollection` re-validates the records and reports any unreadable /
|
|
786
819
|
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).
|
|
820
|
+
always follow a batch of direct writes with a `presentCollection` call and
|
|
821
|
+
**act on any ⚠️ it returns**, rather than assuming every record landed.
|
|
822
|
+
(This safety net applies after `putItems` batches too, but direct writes are
|
|
823
|
+
where it earns its keep.)
|
|
801
824
|
|
|
802
825
|
## End-to-end: creating a new collection skill
|
|
803
826
|
|
package/assets/helps/gemini.md
CHANGED
|
@@ -36,19 +36,21 @@ The Gemini API has a **free tier that is sufficient for personal use**. Higher-v
|
|
|
36
36
|
1. Open [Google AI Studio → API keys](https://aistudio.google.com/apikey) and sign in with a Google account.
|
|
37
37
|
2. Click **Create API key**. If prompted, select or create a Google Cloud project (any project will do).
|
|
38
38
|
3. Copy the key — it starts with `AIza…`.
|
|
39
|
-
4.
|
|
39
|
+
4. Create (or open) a `.env` file **in the directory you launch MulmoClaude from** — i.e. the directory where you run `npx mulmoclaude` (in a cloned repo, that's the repo root). Add the line:
|
|
40
40
|
|
|
41
|
-
```
|
|
41
|
+
```dotenv
|
|
42
42
|
GEMINI_API_KEY=AIza…your-key…
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
The key stays in your launch directory, not in the `~/mulmoclaude` workspace (which is managed by the assistant). You can also just `export GEMINI_API_KEY=…` in your shell before launching — an exported value takes precedence over the `.env` file.
|
|
46
|
+
|
|
45
47
|
5. Restart MulmoClaude so the new environment variable is picked up.
|
|
46
48
|
|
|
47
49
|
## Verifying It's Active
|
|
48
50
|
|
|
49
51
|
The quickest check: switch to the **Artist** role and ask for _"an image of a red panda"_. If a real image appears in the canvas (instead of an italic text marker or a disabled-role hint), the key is wired up correctly.
|
|
50
52
|
|
|
51
|
-
You can also inspect the server log on startup:
|
|
53
|
+
You can also inspect the server log on startup: a `GEMINI_API_KEY not set — image / audio / video generation is unavailable` warning means the key was not found (missing, misread, or placed outside your launch directory).
|
|
52
54
|
|
|
53
55
|
## Security
|
|
54
56
|
|
|
@@ -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.
|