@rubytech/create-maxy-code 0.1.138 → 0.1.140
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/package.json +1 -1
- package/payload/platform/config/brand.json +1 -1
- package/payload/platform/lib/obsidian-parser/dist/index.d.ts +98 -0
- package/payload/platform/lib/obsidian-parser/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/obsidian-parser/dist/index.js +480 -0
- package/payload/platform/lib/obsidian-parser/dist/index.js.map +1 -0
- package/payload/platform/lib/obsidian-parser/src/index.ts +572 -0
- package/payload/platform/lib/obsidian-parser/tsconfig.json +8 -0
- package/payload/platform/neo4j/schema.cypher +6 -0
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/.claude-plugin/marketplace.json +10 -0
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
- package/payload/platform/plugins/memory/PLUGIN.md +3 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js +89 -2
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +64 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +258 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.d.ts +127 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.js +477 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/obsidian-vault-import.js.map +1 -0
- package/payload/platform/plugins/notion-import/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/notion-import/PLUGIN.md +27 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/SKILL.md +110 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/attachments.md +55 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/databases.md +81 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/page-tree.md +61 -0
- package/payload/platform/plugins/notion-import/skills/notion-import/references/workspace-export.md +41 -0
- package/payload/platform/plugins/obsidian-import/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/obsidian-import/PLUGIN.md +39 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/SKILL.md +92 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/attachments.md +80 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/daily-notes.md +31 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/vault-structure.md +46 -0
- package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/wikilinks.md +70 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +13 -1
package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/daily-notes.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Daily Notes
|
|
2
|
+
|
|
3
|
+
How the parser identifies date-stamped vault pages and what gets written for them.
|
|
4
|
+
|
|
5
|
+
## Detection heuristic
|
|
6
|
+
|
|
7
|
+
Three signals in priority order:
|
|
8
|
+
|
|
9
|
+
1. **Frontmatter date.** If the page's YAML frontmatter contains `date:` or `created:` whose value starts with an ISO date (`YYYY-MM-DD…`), that date wins. This is the most explicit signal — operators who run Obsidian's Templater or Daily Notes core plugin typically stamp `date:` automatically.
|
|
10
|
+
2. **Filename matches `YYYY-MM-DD.md`.** Any page whose basename matches exactly the ISO-date pattern is treated as a daily note regardless of folder. This is the default Obsidian Daily Notes naming convention.
|
|
11
|
+
3. **Filename matches `YYYY-MM-DD` inside a known daily-notes folder.** Folder name (case-insensitive) is one of `daily`, `daily-notes`, `daily notes`, `journal`, `journals`. A bare `YYYY-MM-DD.md` is required; arbitrary content inside a journal folder is not auto-stamped.
|
|
12
|
+
|
|
13
|
+
Signals are evaluated in that order; the first match wins. No signal → `noteDate` is null.
|
|
14
|
+
|
|
15
|
+
## What gets written
|
|
16
|
+
|
|
17
|
+
When `noteDate` is non-null, the page's `:KnowledgeDocument` carries `noteDate: date('YYYY-MM-DD')` as a typed Neo4j `date` value. This is queryable: `MATCH (k:KnowledgeDocument) WHERE k.noteDate >= date('2026-01-01') RETURN k` returns every daily note from this year, without needing a separate `:Day` label.
|
|
18
|
+
|
|
19
|
+
No `:Day` node is created. The brief originally proposed `(:Page)-[:HAS_NOTE]->(:Day {date})`; the Maxy graph encodes the same information as a property on the page itself, mirroring `:Person.birthDate` and `:Event.eventDate`. Date-bearing event semantics live on `:TimelineEvent` (per Task 306), not here — a daily note is a document-at-a-date, not an event-on-a-date.
|
|
20
|
+
|
|
21
|
+
## Folder semantics
|
|
22
|
+
|
|
23
|
+
The known daily-notes folder names above are a parser-side fallback only. They are NOT used for filtering — the operator can still filter on `folderPrefixes: ["Daily Notes/"]` separately, which is independent of daily-note detection. A page in `Personal/Diary/2026-03-15.md` matches the filename pattern (signal 2) and gets `noteDate` set, even though `Personal/Diary/` is not in the known list.
|
|
24
|
+
|
|
25
|
+
## False positives
|
|
26
|
+
|
|
27
|
+
A non-journal page named `2026-03-15-meeting-with-adam.md` does NOT match — the filename must be exactly the ISO date with no suffix. The heuristic deliberately errs on the side of missing some daily notes rather than mis-labelling regular pages.
|
|
28
|
+
|
|
29
|
+
## Repeating dates
|
|
30
|
+
|
|
31
|
+
If two pages map to the same `noteDate` (e.g., `Personal/2026-03-15.md` and `Work/2026-03-15.md`), both are written with the same `noteDate` value. Querying `WHERE k.noteDate = date('2026-03-15')` returns both — they are separate atomic notes that happen to share a date.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Vault Structure
|
|
2
|
+
|
|
3
|
+
How the parser discovers what to ingest from an extracted Obsidian vault.
|
|
4
|
+
|
|
5
|
+
## Entry point
|
|
6
|
+
|
|
7
|
+
The operator supplies an `extractedDir` — an absolute path to the root of an extracted vault. The vault zip is unpacked elsewhere (typically by `unzip-attachment` under `{accountDir}/extracted/<id>/`); this skill assumes the path already exists and is a directory.
|
|
8
|
+
|
|
9
|
+
## What counts as a page
|
|
10
|
+
|
|
11
|
+
A vault page is any file under `extractedDir` ending in `.md` (case-insensitive) that is **not** under a hidden directory. Hidden directories — names starting with `.` (`.obsidian/`, `.trash/`, `.git/`) — are skipped at walk time. The `.obsidian/` config tree is metadata about the vault, not vault content.
|
|
12
|
+
|
|
13
|
+
Symlinks pointing outside the vault root, and any path containing a `..` segment, are rejected at the parser boundary as a defence-in-depth measure on top of the unzip sandboxing.
|
|
14
|
+
|
|
15
|
+
## What counts as a folder
|
|
16
|
+
|
|
17
|
+
Every directory under `extractedDir` is walked depth-first. Empty folders are silently skipped. The parser does not infer "this is a Daily Notes folder, this is People" — folder semantics come from filters the operator supplies (see [daily-notes.md](daily-notes.md) for the one exception, where the parser uses folder names as a fallback signal for daily-note detection).
|
|
18
|
+
|
|
19
|
+
## Frontmatter format
|
|
20
|
+
|
|
21
|
+
The parser recognises YAML frontmatter delimited by `---` on the first line. The frontmatter block:
|
|
22
|
+
|
|
23
|
+
- Must start at byte 0 of the file (no BOM, no preceding blank lines).
|
|
24
|
+
- Must close with a line that is exactly `---`.
|
|
25
|
+
- Is parsed as a minimal YAML subset — top-level `key: value` pairs and `key:` followed by `- item` block sequences, plus flow-style `key: [a, b, c]` arrays.
|
|
26
|
+
- Recognised keys: `aliases`, `tags`, `date`, `created`. Other keys are read but ignored.
|
|
27
|
+
- A malformed frontmatter block does NOT abort parsing — the parser falls back to "no frontmatter" and uses the file body verbatim. This is by design: a single broken page does not break the batch.
|
|
28
|
+
|
|
29
|
+
Pages without frontmatter are ingested with empty `aliases` and `tags` fields; their body is the entire file.
|
|
30
|
+
|
|
31
|
+
## Title
|
|
32
|
+
|
|
33
|
+
Title is the filename without `.md`, normalised to NFC unicode form. Folder path is NOT part of the title — only the basename. Two pages can share a title (`Projects/Maxy.md` and `People/Adam.md/Maxy.md`); they MERGE separately because the natural key is the full `obsidianPath`, not the title.
|
|
34
|
+
|
|
35
|
+
## Content hash
|
|
36
|
+
|
|
37
|
+
Every page contributes a SHA-256 hash of its on-disk bytes. The hash is the idempotency key on re-import: a page whose hash matches the existing graph's `:KnowledgeDocument.contentHash` is skipped during commit. Re-importing the exact same vault is a no-op write.
|
|
38
|
+
|
|
39
|
+
## Out-of-scope file types
|
|
40
|
+
|
|
41
|
+
- `.canvas` files — Obsidian Canvas graphs are JSON, not markdown. Not ingested.
|
|
42
|
+
- `.base` files — Obsidian Bases are not ingested.
|
|
43
|
+
- Loose attachment files (`.png`, `.pdf`, …) not referenced by any `.md` page are not ingested. They're only picked up via the embed syntax in [attachments.md](attachments.md).
|
|
44
|
+
- `.obsidian/` config files — never ingested.
|
|
45
|
+
|
|
46
|
+
Adding any of these means extending the parser library (`platform/lib/obsidian-parser/`); file a task before improvising.
|
package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/wikilinks.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Wikilinks
|
|
2
|
+
|
|
3
|
+
How `[[…]]` text inside an Obsidian note becomes a graph edge.
|
|
4
|
+
|
|
5
|
+
## Syntax surface
|
|
6
|
+
|
|
7
|
+
The parser recognises four forms:
|
|
8
|
+
|
|
9
|
+
| Form | Components | Example |
|
|
10
|
+
|------|-----------|---------|
|
|
11
|
+
| Bare | target | `[[Adam]]` |
|
|
12
|
+
| With anchor | target, anchor | `[[Project Maxy#Goals]]` |
|
|
13
|
+
| With alias | target, alias | `[[Adam Langley\|Adam]]` |
|
|
14
|
+
| Embed | target (routed to attachments OR page-embed) | `![[Diagram.png]]` |
|
|
15
|
+
|
|
16
|
+
`anchor` is the heading slug after `#`; `alias` is the display text after `|`. The parser records both alongside the resolved target so the resulting edge carries the original linkText, alias, and anchor as properties.
|
|
17
|
+
|
|
18
|
+
## Code-fence isolation
|
|
19
|
+
|
|
20
|
+
`[[…]]` inside fenced code blocks (` ``` ` or `~~~`) is NOT parsed. Operators paste code samples into notes; markdown inside those samples should not become graph edges. The fence-aware scan splits the body into code and non-code segments before regex matching.
|
|
21
|
+
|
|
22
|
+
## Resolution priority
|
|
23
|
+
|
|
24
|
+
For every wikilink, the parser computes two candidate sets and chooses by these rules:
|
|
25
|
+
|
|
26
|
+
1. **Intra-vault page candidates.** Match the target text against (a) page titles (basename without `.md`) and (b) declared aliases (frontmatter `aliases:`). Both lookups are case-insensitive after NFC normalisation. A path-form target (`Folder/Sub/Page` or `Folder/Page.md`) bypasses title/alias lookup and resolves directly to that exact path if it exists.
|
|
27
|
+
2. **Existing entity candidates.** Query `:Person`, `:Organization`, `:Concept` nodes scoped to the account where `name = target` (exact, distance 0) or where `levenshtein(name, target) ≤ 2` (fuzzy fallback). The entity index is built once per dry-run from the live graph state.
|
|
28
|
+
|
|
29
|
+
Decision matrix:
|
|
30
|
+
|
|
31
|
+
| Page hits | Exact entity hits | Fuzzy entity hits | Outcome |
|
|
32
|
+
|-----------|-------------------|-------------------|---------|
|
|
33
|
+
| 1 | 0 | 0 | Resolved: page |
|
|
34
|
+
| 0 | 1 | — | Resolved: entity (exact) |
|
|
35
|
+
| 0 | 0 | 1 | Resolved: entity (fuzzy) |
|
|
36
|
+
| 0 | 0 | 0 | Resolved: stub (`:Concept`) |
|
|
37
|
+
| ≥1 | ≥1 | — | **Ambiguous** — operator decides |
|
|
38
|
+
| ≥2 | 0 | — | **Ambiguous** — operator decides |
|
|
39
|
+
| 0 | ≥2 | — | **Ambiguous** — operator decides |
|
|
40
|
+
| 0 | 0 | ≥2 | **Ambiguous** — operator decides |
|
|
41
|
+
|
|
42
|
+
Ambiguities surface in the dry-run plan with a stable `id`. The operator picks one outcome per ambiguity; that decision flows into the commit phase via the `resolutions` map.
|
|
43
|
+
|
|
44
|
+
## Edge kinds
|
|
45
|
+
|
|
46
|
+
| Resolved kind | Edge | Cypher target |
|
|
47
|
+
|---------------|------|---------------|
|
|
48
|
+
| Page | `:WIKILINKS_TO` | `(:KnowledgeDocument)-[:WIKILINKS_TO]->(:KnowledgeDocument)` |
|
|
49
|
+
| Entity | `:MENTIONS` | `(:KnowledgeDocument)-[:MENTIONS]->(:Person\|:Organization\|:Concept)` |
|
|
50
|
+
| Stub | `:MENTIONS` | `(:KnowledgeDocument)-[:MENTIONS]->(:Concept {source:'obsidian-stub'})` |
|
|
51
|
+
|
|
52
|
+
Every edge carries `linkText`, `alias`, `anchor` (page edges only), `source='obsidian'`, `createdAt`.
|
|
53
|
+
|
|
54
|
+
## Why stubs exist
|
|
55
|
+
|
|
56
|
+
A wikilink to "Aaron" where no vault page and no graph entity matches still represents the operator's intent: this note is about Aaron, whoever Aaron is. Dropping the edge would lose information; demanding a hard match would force the operator to pre-populate the graph before importing.
|
|
57
|
+
|
|
58
|
+
Stubs are explicitly distinguishable: `:Concept {source:'obsidian-stub'}`. A later session can promote a stub to a typed `:Person` or `:Organization` via `memory-update`, at which point the `:MENTIONS` edge remains pointed at the same node and the stub flag is removed.
|
|
59
|
+
|
|
60
|
+
## Self-references
|
|
61
|
+
|
|
62
|
+
`[[Self]]` inside `Self.md` produces a self-edge. Obsidian treats it as valid; the parser preserves it.
|
|
63
|
+
|
|
64
|
+
## Anchors
|
|
65
|
+
|
|
66
|
+
`[[Page#section]]` resolves on `Page` only — the `section` portion is stored on the edge as `anchor`. The parser does not create per-heading nodes; section decomposition is out of scope for vault pages (they are atomic notes).
|
|
67
|
+
|
|
68
|
+
## Embeds vs wikilinks
|
|
69
|
+
|
|
70
|
+
`![[file]]` with an attachment extension (`.png`, `.pdf`, `.mp3`, etc.) is treated as an attachment, not a wikilink. `![[Another Note]]` (no attachment extension) is treated as a wikilink — Obsidian renders it as a transclusion, and the cross-reference is preserved as a `:WIKILINKS_TO` / `:MENTIONS` edge depending on resolution.
|
|
@@ -3,7 +3,7 @@ name: database-operator
|
|
|
3
3
|
description: "Graph-write executor reachable from admin via the Task tool. Admin names what should land in the graph; database-operator reads the schema, decides operations, executes them against the memory graph, and reports `{label, elementId}` back. (Reachable historically also from the now-dormant Stop hook in `platform/plugins/admin/hooks/turn-completed-graph-write.sh`; that path is preserved as infrastructure but no longer registered, see Task 214.)"
|
|
4
4
|
summary: "Executes graph writes on admin's behalf when delegated via the Task tool."
|
|
5
5
|
model: claude-sonnet-4-6
|
|
6
|
-
tools: Read, mcp__plugin_memory_memory__memory-write, mcp__plugin_memory_memory__memory-update, mcp__plugin_memory_memory__memory-search, mcp__plugin_memory_memory__memory-typed-edge-pass, mcp__plugin_memory_memory__session-retrospective-skip-rate, mcp__plugin_memory_memory__profile-update, mcp__plugin_memory_memory__profile-read, mcp__plugin_contacts_contacts__contact-create, mcp__plugin_contacts_contacts__contact-update, mcp__plugin_work_work__work-create, mcp__plugin_work_work__work-update, mcp__plugin_work_work__project-create, mcp__plugin_work_work__project-update
|
|
6
|
+
tools: Read, mcp__plugin_memory_memory__memory-write, mcp__plugin_memory_memory__memory-update, mcp__plugin_memory_memory__memory-search, mcp__plugin_memory_memory__memory-typed-edge-pass, mcp__plugin_memory_memory__session-retrospective-skip-rate, mcp__plugin_memory_memory__profile-update, mcp__plugin_memory_memory__profile-read, mcp__plugin_memory_memory__obsidian-vault-import, mcp__plugin_contacts_contacts__contact-create, mcp__plugin_contacts_contacts__contact-update, mcp__plugin_work_work__work-create, mcp__plugin_work_work__work-update, mcp__plugin_work_work__project-create, mcp__plugin_work_work__project-update
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
You are an expert Neo4J graph operator. Admin delegates a graph-write to you through the Task tool; the prompt admin sends names what should land in the graph in plain English.
|
|
@@ -24,6 +24,18 @@ Decide the precise operations against the schema, execute them with the writers
|
|
|
24
24
|
|
|
25
25
|
When admin's brief names the typed-edge pass (Task 305) and supplies a `sinceIso` timestamp, call `memory-typed-edge-pass` with that timestamp exactly once. The tool reads prose-bearing nodes (Message/Page/Meeting/Email/Idea/Note/Post/Report) the account wrote since `sinceIso`, asks Haiku for typed-edge proposals against a closed allowlist, validates each one, and MERGEs accepted edges with `createdBy` provenance. Return the full counter object verbatim — admin needs `nodesProcessed` and `accepted` for the `session-retrospective-mark-complete` call. Do not reinterpret the counts or summarise them.
|
|
26
26
|
|
|
27
|
+
## External-archive ingestion
|
|
28
|
+
|
|
29
|
+
The database-operator also owns ingestion of bulk external archives that map into the graph. Each archive type has its own skill that drives the operator-conversation surface; the writes route through deterministic MCP tools the parser owns server-side. The skills load themselves into your prompt when the operator's request names an archive directory or file.
|
|
30
|
+
|
|
31
|
+
- **Obsidian vault import** — when the operator points at an extracted Obsidian vault directory ("import my Obsidian notes", "ingest this vault at `<path>`"), load [platform/plugins/obsidian-import/skills/obsidian-import/SKILL.md](../../../plugins/obsidian-import/skills/obsidian-import/SKILL.md) and run the two-phase flow (owner confirmation → `mcp__plugin_memory_memory__obsidian-vault-import` `mode='dry-run'` → operator disambiguation → `mode='commit'` with resolutions). Parsing, content-hash idempotency, and attachment staging happen entirely inside the tool — you never read the markdown.
|
|
32
|
+
|
|
33
|
+
Other archive types (LinkedIn export, Notion export, Logseq, Roam, gbrain-repo, Gmail) live under separate plugins owned by `specialists:librarian`; they are not on this surface. If the operator's request points at one of those, hand back to admin so it can dispatch the right specialist.
|
|
34
|
+
|
|
27
35
|
## Skip-rate visibility (session-start)
|
|
28
36
|
|
|
29
37
|
When admin's brief names the skip-rate check, call `session-retrospective-skip-rate` once and return its result verbatim. The tool reports how many of the last N sessions for this account closed without firing the retrospective. Admin uses the rate to inform the operator that closing via Sidebar Archive or tab-close defers the typed-edge pass to the next `/end`. Pass the `windowSize` admin provides; default 10 is fine when absent.
|
|
38
|
+
|
|
39
|
+
## External-archive ingestion
|
|
40
|
+
|
|
41
|
+
When admin's brief names a Notion workspace export — a directory with `Page Title <32-hex>.md` files and paired `<DB Name> <32-hex>.csv` / directory pairs at any depth, or a `Export-*.zip` path — load `platform/plugins/notion-import/skills/notion-import/SKILL.md` and follow it. The skill confirms the importing `:AdminUser` and the workspace label before any file is read, walks the export per its four references (workspace-export, databases, page-tree, attachments), and writes via your existing `memory-write` / `memory-update` / `memory-search` surface. Every node carries `source='notion'`, `importId`, and the standard provenance set. Unmapped Notion relations land as `:MENTIONS` with `mentionContext: '<relation-name>'`; never invent new edge types. Inline `@person` mentions resolve against `:Person {accountId}` only — account-isolation is load-bearing. Per-node writes today; a follow-up at [`.tasks/gbrain/ingestion/395-notion-bulk-archive-handler.md`](../../../../.tasks/gbrain/ingestion/395-notion-bulk-archive-handler.md) adds a batched `memory-archive-write` path.
|