@rubytech/create-maxy-lite 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/index.mjs +43 -22
  2. package/lib/healthcheck.mjs +60 -19
  3. package/lib/orchestrate.mjs +32 -11
  4. package/lib/paths.mjs +73 -0
  5. package/package.json +1 -1
  6. package/payload/package.json +2 -1
  7. package/payload/skills/README.md +26 -0
  8. package/payload/skills/admin/datetime/SKILL.md +147 -0
  9. package/payload/skills/admin/session-management/SKILL.md +39 -0
  10. package/payload/skills/admin/upgrade/SKILL.md +32 -0
  11. package/payload/skills/browser/SKILL.md +60 -0
  12. package/payload/skills/browser/scripts/cdp.mjs +134 -0
  13. package/payload/skills/browser/scripts/pdf.mjs +38 -0
  14. package/payload/skills/browser/scripts/render.mjs +43 -0
  15. package/payload/skills/browser/scripts/screenshot.mjs +52 -0
  16. package/payload/skills/business-assistant/SKILL.md +110 -0
  17. package/payload/skills/calendar-site/SKILL.md +71 -0
  18. package/payload/skills/calendar-site/template/availability.json +14 -0
  19. package/payload/skills/calendar-site/template/functions/api/book.ts +112 -0
  20. package/payload/skills/calendar-site/template/public/booking.css +100 -0
  21. package/payload/skills/calendar-site/template/public/booking.js +202 -0
  22. package/payload/skills/calendar-site/template/public/index.html +44 -0
  23. package/payload/skills/calendar-site/template/schema.sql +19 -0
  24. package/payload/skills/calendar-site/template/wrangler.toml +14 -0
  25. package/payload/skills/contacts/SKILL.md +57 -0
  26. package/payload/skills/deep-research/SKILL.md +70 -0
  27. package/payload/skills/deep-research/references/citation-styles.md +52 -0
  28. package/payload/skills/deep-research/references/research-modes.md +22 -0
  29. package/payload/skills/deep-research/references/search-strategy.md +24 -0
  30. package/payload/skills/docs/SKILL.md +23 -0
  31. package/payload/skills/docs/references/capability-map.md +25 -0
  32. package/payload/skills/docs/references/getting-started.md +29 -0
  33. package/payload/skills/docs/references/vault-model.md +40 -0
  34. package/payload/skills/email-composition/SKILL.md +107 -0
  35. package/payload/skills/memory/SKILL.md +48 -0
  36. package/payload/skills/projects/SKILL.md +47 -0
  37. package/payload/skills/publish-site/SKILL.md +21 -0
  38. package/payload/skills/replicate/SKILL.md +63 -0
  39. package/payload/skills/replicate/scripts/replicate-image.mjs +131 -0
  40. package/payload/skills/scheduling/SKILL.md +74 -0
  41. package/payload/skills/site-deploy/SKILL.md +52 -0
  42. package/payload/skills/slides/SKILL.md +45 -0
  43. package/payload/skills/slides/deck.html +1359 -0
  44. package/payload/skills/url-get/SKILL.md +48 -0
  45. package/payload/skills/url-get/scripts/url-get.mjs +93 -0
  46. package/payload/skills/work/SKILL.md +49 -0
  47. package/payload/webchat/inject-line.mjs +11 -0
  48. package/payload/webchat/package.json +2 -1
  49. package/payload/webchat/request-handler.mjs +62 -0
  50. package/payload/webchat/server.mjs +31 -31
@@ -0,0 +1,22 @@
1
+ # Research Modes
2
+
3
+ Adjust behaviour based on query type. Select the appropriate mode after Phase 1 decomposition.
4
+
5
+ ## General Research / Fact-finding
6
+
7
+ - 3–5 sources is usually sufficient
8
+ - Prioritise recency and source authority
9
+ - Lead with the direct answer, then supporting evidence
10
+
11
+ ## Competitive / Market Intelligence
12
+
13
+ - Always cover: (a) the company's own claims, (b) independent analyst or press coverage, (c) user sentiment (G2, Reddit, Trustpilot where relevant)
14
+ - Structure output as: Overview, Key differentiators, Weaknesses/risks, Pricing (if findable), Verdict
15
+ - Flag if pricing is not publicly listed
16
+
17
+ ## Academic / Deep Literature Review
18
+
19
+ - Prioritise peer-reviewed sources, preprints (arXiv, bioRxiv), and institutional reports
20
+ - Note study size, methodology, and date for each source
21
+ - Distinguish between consensus findings and contested/emerging claims
22
+ - Use deep search depth. Do not stop at 3 sources for academic queries
@@ -0,0 +1,24 @@
1
+ # Search Strategy and Source Evaluation
2
+
3
+ ## Query Crafting (Phase 2)
4
+
5
+ Map each sub-question to one or more targeted search queries:
6
+
7
+ - Keep queries short (3–6 words). Specific nouns beat vague phrases.
8
+ - Vary query framing: try the direct question, a "site:domain" form, and a comparison form where relevant.
9
+ - For competitive intelligence: always search the target company directly AND search for independent reviews/comparisons.
10
+ - For literature/academic queries: search for primary papers, then for secondary summaries.
11
+ - For fast-changing topics (pricing, funding, personnel): append the current year to queries.
12
+ - Never repeat the same query twice. If a search returns poor results, rephrase. Do not retry identical terms.
13
+
14
+ ## Source Fetching and Extraction (Phase 3)
15
+
16
+ For each source returned:
17
+
18
+ 1. Fetch the full page content where possible, not just the snippet.
19
+ 2. Extract only what is relevant to the sub-question that drove this search.
20
+ 3. Note the publication date if visible.
21
+ 4. Flag sources older than 18 months as `[possibly outdated]`.
22
+ 5. Prefer: official docs, primary research, reputable press. Deprioritise: forums, SEO farms, undated pages.
23
+
24
+ Do not quote large blocks verbatim. Paraphrase and extract the key claim or data point.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: docs
3
+ description: >
4
+ Reference documentation for how maxy-lite works, loaded on demand. Use when the
5
+ owner asks how the assistant works, what it can do, where their files are, how
6
+ to get started, or for help. Trigger phrases: "how does this work", "what can
7
+ you do", "where are my files", "help", "getting started", "explain maxy",
8
+ "how is my data stored".
9
+ ---
10
+
11
+ # docs
12
+
13
+ A small set of reference documents describing how maxy-lite works. Do not load them all. Read the one that answers the owner's question with the `Read` tool, then answer from it.
14
+
15
+ ## References
16
+
17
+ - **`references/getting-started.md`** Read this for a new owner, or for "how do I use this", "what can you do" at a high level, "help", or general orientation. Plain-language, owner-facing.
18
+ - **`references/capability-map.md`** Read this for "what can you do" in detail, "what tools do you have", "can you do X", or to understand which capabilities exist and in what form.
19
+ - **`references/vault-model.md`** Read this for "where is my data", "how is it stored", "what is the vault", anything about file layout, entity types, areas, links, or the validator. The technical model of the store.
20
+
21
+ ## Boundaries
22
+
23
+ These references describe lite's own reality: a single assistant, a file-native vault, on one device. They are not the platform documentation for the larger graph-backed product. For the exact field-by-field schema, point to `schema/SCHEMA.md` rather than restating it.
@@ -0,0 +1,25 @@
1
+ # What maxy-lite can do
2
+
3
+ maxy-lite is a single assistant that organises a person's files and helps with everyday tasks, all on their own device. Its capabilities take one of a few forms, chosen to keep the device light.
4
+
5
+ ## The forms
6
+
7
+ - **Native file operations.** Reading, writing, and editing vault files directly. This covers all store work: contacts, notes, tasks, projects, events, and the filed documents and assets. No tool or process is involved.
8
+ - **Skills with small scripts.** A skill is instructions the assistant reads; some come with a small script run on demand. This is the default for anything that fetches a page, calls a service, or runs a one-off job. Skills are files, so they add no running process.
9
+ - **Official connectors.** Off-the-shelf remote services configured rather than built: Google (Gmail, Calendar, Drive) and Cloudflare.
10
+ - **Channel servers.** The one kind of long-lived process: the messaging channels (Telegram, WhatsApp) that must listen for incoming messages.
11
+
12
+ ## What it can do today
13
+
14
+ - **Store work** over the vault: keep contacts, notes, tasks, projects, a calendar, and filed documents, all as plain files.
15
+ - **Fetch a web page** faithfully as markdown (the `url-get` skill).
16
+ - **Research a topic** across live web sources and save a cited report to the vault (the `deep-research` skill).
17
+ - **Generate an image** from a prompt (the `replicate` skill).
18
+ - **Drive the browser** for the things plain HTTP cannot do: render a JavaScript page, turn HTML into a PDF, take a screenshot (the `browser` skill).
19
+ - **Answer time and date questions** deterministically (the `datetime` skill).
20
+ - **Manage sessions** start a new one, resume a past one, recall what a past one was about (the `session-management` skill).
21
+ - **Upgrade itself** by re-running the installer (the `upgrade` skill).
22
+
23
+ ## What it deliberately does not have
24
+
25
+ No graph database, no per-account separation, no specialist sub-agents, no public-facing agents. One assistant, one file tree, one device. Capabilities that exist only to serve those platform features are not part of lite.
@@ -0,0 +1,29 @@
1
+ # Getting started with maxy-lite
2
+
3
+ A short orientation for a new owner.
4
+
5
+ ## What it is
6
+
7
+ maxy-lite is a personal assistant that runs entirely on your own device. It keeps your information as ordinary files and helps you with everyday tasks. One assistant does everything; there is nothing to log into and nothing leaves your phone unless you ask it to.
8
+
9
+ ## Where your files live
10
+
11
+ Everything is stored as plain markdown files in your vault (by default `~/.maxy-lite/vault`). Each file is one thing: a contact, a note, a task, an event, a bill. You can open and edit the same files in Obsidian on your phone, so you are never locked out of your own data.
12
+
13
+ ## How to use it
14
+
15
+ Just ask in plain language. The assistant picks the right tool for the job.
16
+
17
+ - **Keep things organised:** "add Jane Smith to my contacts", "make a note about the boiler service", "remind me to renew the car insurance", "what's on my calendar next week".
18
+ - **Research:** "what's the latest on X", "compare these two options", "look into this and save me a summary". It searches the web, reads the sources, and saves a cited note to your vault.
19
+ - **Read a web page:** give it a link and ask what it says.
20
+ - **Make an image:** "generate a logo for my side project", "make a picture of a mountain lake".
21
+ - **Build a document or page:** ask for a deck, a brochure, or a simple website, and it turns the result into a PDF or publishes it.
22
+
23
+ ## Sessions
24
+
25
+ Each conversation is one session, saved as a transcript file. You can start fresh any time, or ask the assistant to pick up where you left off. Before you clear a conversation, ask it to save anything important to the vault, since a new session only remembers what is written to files.
26
+
27
+ ## Keeping it current
28
+
29
+ Ask the assistant to upgrade when you want the latest version. It re-runs the installer and the new version takes effect on your next session.
@@ -0,0 +1,40 @@
1
+ # The vault model
2
+
3
+ maxy-lite stores everything as plain markdown files in one folder tree, the vault. There is no database. Each file is one record: YAML frontmatter holds the structured fields, the markdown body holds the prose. This is the same substrate Obsidian reads, so the owner can browse and edit the same files on their phone.
4
+
5
+ ## Two organising dimensions
6
+
7
+ - **`type`** (a frontmatter field) says what kind of record a file is. It drives which fields the validator checks. It does not depend on the folder.
8
+ - **Area** says which part of life the record belongs to. It drives where the file is filed. The areas are a fixed list: `home`, `work`, `finance`, `health`, `vehicle`, `insurance`, `travel`, `education`, `family`, `legal`, `personal`.
9
+
10
+ Every document, asset, and project must declare an `area`. Contacts, events, and activities may.
11
+
12
+ ## Layout
13
+
14
+ - **Global folders** (cross-area): `people/`, `organizations/`, `calendar/`, `activities/`. These are the address book and calendar.
15
+ - **Area folders** (the filing cabinet): one folder per area, holding typed documents and assets.
16
+ - **`projects/`** holds projects.
17
+
18
+ ## Entity families
19
+
20
+ - **Contacts and orgs:** Person (vCard fields), Organization.
21
+ - **Time:** Event (iCalendar fields).
22
+ - **Work:** Task, Project.
23
+ - **Activity:** Note, Email, Call, Meeting. The long-form content (note body, email text, call notes) lives in the markdown body, not in a field.
24
+ - **Assets:** Property, Vehicle, Account.
25
+ - **Financial records:** Invoice, Expense, Bill, Statement, Payment.
26
+ - **Life documents:** Policy, Contract, Warranty, TaxDocument, IdentityDocument.
27
+
28
+ The full field-by-field declaration is `schema/SCHEMA.md`. Read that for the exact required and optional fields of each entity. Do not duplicate it here.
29
+
30
+ ## Links
31
+
32
+ A field whose value is `"[[Target]]"` (or a bare filename) is an association to another file, resolved by basename. Keep filenames unique across folders so links resolve unambiguously.
33
+
34
+ ## The validator
35
+
36
+ `validator/cli.mjs` checks a vault against `SCHEMA.md` with no model in the path. It flags missing required fields, wrong field types, bad area values, and links that resolve to no file or to the wrong entity type. A data skill writes vault files, then runs the validator and expects `invalid=0`, exit 0. A non-zero exit means a file violates the schema; fix it before claiming the data is saved.
37
+
38
+ ```bash
39
+ node ~/.maxy-lite/validator/cli.mjs ~/.maxy-lite/vault
40
+ ```
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: email-composition
3
+ description: Draft and send email, and keep a record of what was sent in the vault. Use when the user asks to reply to a message, write a new email, follow up on a thread, or triage what is outstanding in the inbox (for example "reply to Sarah about the lease", "email Jane the quote", "what needs dealing with in my inbox"). Every send is approved by the user first, and every sent message is recorded as a vault Email associated to the person it went to.
4
+ ---
5
+
6
+ # Email composition
7
+
8
+ A conversational wrapper around the Gmail connector. Drafting, replying, follow-ups, triage, and recording the result. Human in the loop only: every send is approved by the user before it goes. There is no drip sequence, no scheduled send, no broadcast, and no automatic categorisation.
9
+
10
+ Two things make this skill more than a thin wrapper: the user approves before anything leaves, and after a send the message is written back into the vault as an Email so the correspondence is part of the store, not lost in the provider. The Email entity is in [`SCHEMA.md`](../../schema/SCHEMA.md) under *Activities*.
11
+
12
+ ## Transport: the Gmail connector
13
+
14
+ Outbound mail goes through the **Gmail connector** (the official Google remote MCP added on the device). Use whichever send, draft, and read tools that connector exposes; this skill names them by capability, not by a fixed tool name, because the connector is configured on the device and its tool surface belongs to the provider.
15
+
16
+ If the Gmail connector is not connected yet, do the whole draft and approval flow, then tell the user the message is ready but the Gmail connector still needs connecting before it can be sent, and stop. Do not invent a send. Connecting the connector is the connectors setup, not this skill.
17
+
18
+ ## When to use
19
+
20
+ - **Reply to a thread.** "Reply to Sarah's email about the lease."
21
+ - **New outbound email.** "Email Jane the quote." "Send the team a note about Friday."
22
+ - **Follow up.** "Follow up with everyone from yesterday."
23
+ - **Triage.** "What's outstanding in my inbox?"
24
+
25
+ Do not use it for listing or searching email as the end goal (a read tool answers that directly), or for any automated send loop. If the user asks for drip, scheduled, or broadcast sending, decline and say this skill is human in the loop only.
26
+
27
+ ## Who it is going to: resolve the person first
28
+
29
+ The recorded Email links to the recipient with a `participants` link, and that link fails validation if the person's file does not exist. So settle the recipient before sending:
30
+
31
+ - Find the contact by Grepping `people/` for the name or address, exactly as the `contacts` skill describes (`grep -rl "jane@acme.example" people/`).
32
+ - If the recipient is not in the vault yet, create the Person file first (via the `contacts` skill) so the link will resolve when you record the send.
33
+
34
+ ## Flow: reply to a thread
35
+
36
+ 1. **Find the thread.** Use the connector's read or search tool to pull the message and its parents. If several threads match the user's description, show three to five candidates with sender, subject, and date, and ask which one. Never guess. If nothing matches, ask the user to refine rather than inventing a thread.
37
+ 2. **Read the context.** Reply to the actual question in the latest message, not a one line summary. If the thread has attachments the reply refers to, name them and ask whether to forward.
38
+ 3. **Skip the ones that should be skipped.** An out of office autoreply, an unsubscribe or list message: do not draft. Flag it to the user and move on. A hostile or grief toned message: draft carefully and say so in the presentation, so the user reviews it closely before sending.
39
+ 4. **Draft.** Keep it tight. Anchor to the specific question, and to any decision the user has already stated in this conversation. Default to a neutral, polite British business register unless the user has asked for another tone.
40
+ 5. **Present.** Show the draft with the recipient, the subject, the body, and any flags from step 3. Do not send at this point. The send is not approved yet.
41
+ 6. **Edit loop.** The user approves ("send it"), edits verbally ("make it shorter", "say yes to the price, not the timeline"), or abandons. Apply edits and re present until they approve or abandon.
42
+ 7. **Send, on explicit approval.** Call the connector's reply or send tool so the message threads correctly. If it errors, show the error as is and let the user decide whether to fix and retry or abandon. Do not retry silently.
43
+ 8. **Record the send.** See "Record every send" below. This is not optional.
44
+
45
+ ## Flow: new outbound email
46
+
47
+ The same as a reply, with two differences: there is no thread to load, so ask the user for the recipient, the topic, and any constraints; and you send a fresh message rather than a reply. If the user names a group ("email the team") rather than one person, ask which addresses to include; list candidates you can resolve from `people/`, and do not invent recipients.
48
+
49
+ ## Flow: leave it in drafts
50
+
51
+ When the user wants a reviewable, unsent message ("draft it", "leave it in drafts", "don't send yet"), run the same present and edit loop, but save the approved draft through the connector's draft tool instead of sending. Nothing is dispatched. A saved draft is not a send, so it is not recorded as a vault Email until it is actually sent.
52
+
53
+ ## Flow: triage
54
+
55
+ When the user asks what is outstanding, read the last several unread messages and return a one line summary of each with a recommended action: reply, file, or ignore (autoreplies, newsletters, and list mail are ignore). Then wait. Triage never auto drafts; when the user picks one, drop into the reply flow for that message.
56
+
57
+ ## Record every send
58
+
59
+ After the connector confirms a send, write the message into the vault as an Email so the correspondence is part of the store. Without this step the send leaves no trace in the vault, which is lost provenance and the main failure this skill exists to prevent.
60
+
61
+ Write `activities/<subject>.md`:
62
+
63
+ ```yaml
64
+ ---
65
+ type: email
66
+ subject: Quote for the bathroom refit
67
+ timestamp: 2026-06-21T14:00:00
68
+ direction: outgoing
69
+ participants:
70
+ - "[[Jane Doe]]"
71
+ area: work
72
+ ---
73
+ Hi Jane, here is the quote we discussed.
74
+ ```
75
+
76
+ Required is `type: email` and `subject`. `timestamp` is the time of the send; `direction` is `outgoing` for a message you sent (use `incoming` if you are recording one that arrived). `participants` is a list of links to the people on the message, each of whom must already have a Person file. Optionally, `about` links the email to whatever it concerns (a project, a contact, anything), in which case that target file must already exist, and `area` files it under one life area. The body of the file is the email text. The field list is in [`SCHEMA.md`](../../schema/SCHEMA.md).
77
+
78
+ Then validate:
79
+
80
+ ```sh
81
+ maxy-lite-validate "$HOME/maxy"
82
+ ```
83
+
84
+ It exits 0 only when every file conforms. If the Email you just wrote is `ok=false`, the bracketed error names the field: `subject:missing` (the required subject is absent), `participants:dangling` (a linked person has no file, so create it), `participants:target` (a link points at a non person file), `area:area` (an area outside the controlled vocabulary). Fix the named field and re run until it is `ok=true`. Never leave a sent message unrecorded or a recorded Email in a failing state.
85
+
86
+ ## Edge cases
87
+
88
+ | Situation | What to do |
89
+ |-----------|------------|
90
+ | Several threads match the description | Show three to five candidates and ask. Never guess. |
91
+ | Recipient not in the vault yet | Create the Person file first, then send and record, so the link resolves. |
92
+ | Original thread has people in cc | Reply to the sender only by default. Ask before copying others. |
93
+ | Inbound is an out of office or list message | Do not draft. Flag it and move on. |
94
+ | Inbound is hostile or grief toned | Draft carefully and flag the tone. The user reviews before sending. |
95
+ | The connector returns an error after approval | Show the error as is. The user decides whether to fix and retry or abandon. No silent retry. |
96
+ | The Gmail connector is not connected | Draft and present, then say the connector needs connecting before the send. Do not fake a send. |
97
+
98
+ ## Out of scope
99
+
100
+ Never offer these here, even if asked:
101
+
102
+ - **Drip sequences** and any multi step automated send chain.
103
+ - **Scheduled or delayed sends.** The user triggers the send when they approve.
104
+ - **List broadcasts** to managed subscriber lists.
105
+ - **Automatic inbox labelling or categorisation.** Triage is on demand only.
106
+ - **New tools.** This skill orchestrates the Gmail connector and writes vault files. Nothing else.
107
+ </content>
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: memory
3
+ description: Remember and recall facts across sessions. Use when the user says to remember something, asks what you know about a person or topic, refers to something told to you earlier, or when you need to recover context at the start of a session. The vault is the memory; this skill is the convention for writing facts into it and reading them back.
4
+ ---
5
+
6
+ # Memory
7
+
8
+ There is no separate memory store. The vault is the memory: every fact you need to keep lives as frontmatter or body text in a vault file, and recall is reading those files back. This skill is thin on purpose. It codifies two things: where a fact goes when you learn it, and how to find a fact when you need it. The entity field lists are in [`SCHEMA.md`](../../schema/SCHEMA.md); the `contacts`, `work`, and `projects` skills own their respective writes.
9
+
10
+ ## Where a new fact goes
11
+
12
+ A fact almost always belongs to something that already has a file. Put it there, not in a parallel store:
13
+
14
+ - A fact **about a person or organization** (a preference, a relationship, a detail) goes in the body of that contact's file, or as a typed field if the SCHEMA has one for it (a new email goes in `emails`, a birthday in `birthday`). Edit the existing file.
15
+ - A fact that **is** a thing the SCHEMA already models (a task to do, an event, a document) is created with the matching skill, not written as loose memory.
16
+ - A standalone fact that belongs to **no existing entity** becomes a **Note**: a file in `activities/` with `type: note`, a `title`, and the fact in the markdown body. Link it to whatever it concerns with `about` so it is reachable by traversal.
17
+
18
+ ```yaml
19
+ ---
20
+ type: note
21
+ title: Jane wine preference
22
+ about: "[[Jane Doe]]"
23
+ area: personal
24
+ ---
25
+ Jane prefers Rioja. Mentioned at the conference dinner.
26
+ ```
27
+
28
+ Prefer enriching an existing file over creating a new note. A note is for a fact with no better home.
29
+
30
+ ## How to recall
31
+
32
+ Recall is search over the vault, in this order:
33
+
34
+ 1. **Grep the frontmatter and body** for the term: `grep -rl "Rioja" .` finds every file mentioning it; `grep -rl "name: Jane" people/` finds the contact.
35
+ 2. **Read the matched file** for the full record.
36
+ 3. **Follow the links** to pull in connected context: a note's `about`, a person's `org`, a task's `project`. Traversal is following basenames between files.
37
+
38
+ At the start of a session with no context, recall is the same move: Grep for the names or topics the user raises, read what comes back, and follow the links. Nothing is remembered that is not in a file, so if a fact is not found by Grep it was never written; write it.
39
+
40
+ ## After writing a fact, validate
41
+
42
+ When a fact is written as a new note or by editing an entity file, run the deterministic validator over the vault:
43
+
44
+ ```sh
45
+ maxy-lite-validate "$HOME/maxy"
46
+ ```
47
+
48
+ It exits 0 only when every file conforms. If a line names the note you just wrote with `ok=false`, the bracketed error names the violation: `title:missing` (a note needs a title), `area:area` (an area outside the controlled vocabulary), `about:dangling` (the linked file does not exist). Fix the named field and re-run until that file is `ok=true`. A fact left in a failing file is a fact that will not be trusted on recall.
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: projects
3
+ description: Create and track multi-step efforts in the vault and tie tasks, events, and documents to them. Use when the user describes something with several moving parts (a holiday, a house move, a renovation, a launch), asks to start a project, wants to see everything related to one, or wants to file a task or event under a project. Owns the Project entity of the maxy-lite SCHEMA.
4
+ ---
5
+
6
+ # Projects
7
+
8
+ A **Project** is a multi-step effort that ties together the tasks, events, and documents it involves. It is one file in `projects/`: YAML frontmatter plus a markdown body for the plan, status notes, and links. The authoritative field list is [`SCHEMA.md`](../../schema/SCHEMA.md) under *Projects*.
9
+
10
+ ## Project (`projects/<Name>.md`)
11
+
12
+ Required: `type: project`, `name`, and `area` (one of the controlled areas in the SCHEMA). Optional: `status` (`active` / `done` / `someday`), `startDate`, `dueDate` (`YYYY-MM-DD`), `about` (a single link to anything the project centres on). `area` is required for a project, unlike for a contact or task, so always set it.
13
+
14
+ ```yaml
15
+ ---
16
+ type: project
17
+ name: House Move
18
+ area: home
19
+ status: active
20
+ startDate: 2026-06-01
21
+ dueDate: 2026-09-30
22
+ ---
23
+ Moving from the London flat to the house in Bristol. Survey, mortgage, packers, schools.
24
+ ```
25
+
26
+ ## Tying tasks and events to a project
27
+
28
+ A project does not list its members in its own frontmatter. Instead each Task or Event carries a `project` link back to the project. To file an item under a project, edit the item and set its `project` field to the project's basename in wikilink form, e.g. in a task file `project: "[[House Move]]"`. The project file must exist first. The `work` skill owns the Task fields; this skill owns the Project file.
29
+
30
+ ## Seeing everything in a project
31
+
32
+ The links point inward, so Grep for the project's name across the activity and calendar folders:
33
+
34
+ - tasks in a project: `grep -rl "\[\[House Move\]\]" activities/`
35
+ - events in a project: `grep -rl "\[\[House Move\]\]" calendar/`
36
+
37
+ Read the matched files for the detail. To close a project, edit its `status` to `done`; do not delete the file.
38
+
39
+ ## After every write, validate
40
+
41
+ After creating or editing a project file (or after setting a `project` link on a task or event), run the deterministic validator over the vault:
42
+
43
+ ```sh
44
+ maxy-lite-validate "$HOME/maxy"
45
+ ```
46
+
47
+ It exits 0 only when every file conforms. If a line names the project you just wrote with `ok=false`, the bracketed error names the violation: `name:missing` or `area:missing` (a required field is absent), `area:area` (an area outside the controlled vocabulary), `about:dangling` (the linked file does not exist), `startDate:type` or `dueDate:type` (a malformed date). Fix the named field and re-run until that file is `ok=true`. Never leave a project you wrote in a failing state.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: publish-site
3
+ description: Put a built site or page online and hand back a live link. Use when the user says publish, host, put this online, make it live, or share a link to a deck or page. Routes to `site-deploy`, which deploys to Cloudflare Pages so the page stays up with the phone offline.
4
+ ---
5
+
6
+ # Publish a site
7
+
8
+ This is the intent router for "make it live". When the user wants a page online, you confirm what to publish and where the files are, then hand off to [`site-deploy`](../site-deploy/SKILL.md), which does the actual Cloudflare Pages deploy and returns the URL.
9
+
10
+ ## What you need before routing
11
+
12
+ - A folder of static files ready to go: an [`slides`](../slides/SKILL.md) deck (the `.html` file plus its `media/` folder, if any), a landing page, or any HTML tree. If the site is not built yet, build it first; this skill does not generate content.
13
+ - A project slug (lowercase, hyphenated). Offer one derived from the site's name and confirm it with the user, because the slug is the live address.
14
+
15
+ ## Where it gets hosted
16
+
17
+ Cloudflare Pages is the lite default: the page is hosted on Cloudflare's edge and stays reachable when the phone is off. There is one hosting path in lite, so there is nothing to choose between; serving a page from the device itself is not part of lite. Hand the folder and the slug to `site-deploy` and relay the live URL it returns.
18
+
19
+ ## After it is live
20
+
21
+ `site-deploy` gates "done" on a live `200` with the right content and gives back the URL. Surface that URL to the user as the result. If the deploy fails on authentication, the fix is `wrangler login`, per `site-deploy`.
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: replicate
3
+ description: >
4
+ Generate an image from a text prompt. Use when the owner wants a picture,
5
+ logo, illustration, diagram, icon, or any visual created from a description.
6
+ Trigger phrases: "generate an image", "make a picture of", "create a logo",
7
+ "draw me", "design an illustration", "I need a graphic of". Produces a real
8
+ image file in the vault.
9
+ ---
10
+
11
+ # replicate
12
+
13
+ Turn a text prompt into an image using the Replicate API. The script picks the model, calls the API, downloads the result into the vault, and prints the file path.
14
+
15
+ ## When to use
16
+
17
+ The owner wants an image made from a description: a logo, an illustration, a concept sketch, a photoreal scene, a diagram, an icon. This produces an actual file, not a description of one.
18
+
19
+ ## Models
20
+
21
+ Pick the model to match the job:
22
+
23
+ - `nano-banana-pro` (default): photorealistic, data-driven visuals.
24
+ - `recraft-v4`: design compositions and branded assets. The only model that can return `svg`.
25
+ - `flux-schnell`: fast drafts and concept sketches.
26
+
27
+ ## How to run
28
+
29
+ ```bash
30
+ node ~/.maxy-lite/skills/replicate/scripts/replicate-image.mjs \
31
+ --prompt="a calm mountain lake at dawn, soft light" \
32
+ --model=nano-banana-pro \
33
+ --aspect=16:9 \
34
+ --format=png
35
+ ```
36
+
37
+ - `--prompt` (required): what to draw.
38
+ - `--model` (optional, default `nano-banana-pro`): one of the three above.
39
+ - `--aspect` (optional, default `1:1`): one of `1:1`, `4:3`, `3:2`, `16:9`, `21:9`, `9:16`.
40
+ - `--format` (optional, default `png`): `png`, `jpg`, `webp`, or `svg` (svg only with `recraft-v4`).
41
+
42
+ On success the file path prints to stdout and the image is saved under `$LITE_VAULT/Assets/generated/`. Show the owner the path.
43
+
44
+ ## Token
45
+
46
+ The script reads `REPLICATE_API_TOKEN` from the environment, or falls back to `~/.replicate/api-token`. The lite runtime supplies the token from its secrets. If neither is set the script exits with `error=no-token`. Tell the owner a Replicate token is needed and stop.
47
+
48
+ ## Reading the result
49
+
50
+ - **A file path on stdout, exit 0**: the image was generated and saved.
51
+ - **Non-zero exit**: an error on stderr as `[replicate] error=<code>`:
52
+ - `no-token`: no token configured.
53
+ - `auth`: the token was rejected.
54
+ - `network`: the Replicate API was unreachable.
55
+ - `model`: an unknown model, a bad model option, or the generation failed.
56
+ - `download`: the image was generated but could not be fetched.
57
+ - `disk`: the image could not be written to the vault.
58
+
59
+ Surface the error plainly. Never claim an image was made when the script failed.
60
+
61
+ ## Boundaries
62
+
63
+ Generates one image per call. Does not edit existing images, run image-to-image, or upscale. Those need a different model and are not wired here.
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+ // replicate-image — image generation via the Replicate HTTP API. Zero deps.
3
+ //
4
+ // Ported from the maxy-code replicate MCP tool to a single lite CLI script.
5
+ // Uses native fetch against the Replicate predictions API (no replicate SDK, no
6
+ // nanoid). Same three-model registry and aspect-ratio table. The generated image
7
+ // is downloaded into the vault assets directory and its path printed to stdout.
8
+ //
9
+ // Token resolution (parity with maxy-code): REPLICATE_API_TOKEN env first, then
10
+ // ~/.replicate/api-token. Errors go to stderr as [replicate] error=<code> and
11
+ // exit non-zero. Codes: usage, no-token, model, auth, network, download, disk.
12
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
13
+ import { homedir } from "node:os";
14
+ import { join, resolve } from "node:path";
15
+ import { randomUUID } from "node:crypto";
16
+
17
+ const MODELS = {
18
+ "nano-banana-pro": { owner: "google", name: "nano-banana-pro" },
19
+ "recraft-v4": { owner: "recraft-ai", name: "recraft-v4", svg: "recraft-v4-svg" },
20
+ "flux-schnell": { owner: "black-forest-labs", name: "flux-schnell" },
21
+ };
22
+
23
+ const ASPECT = {
24
+ "1:1": { width: 1024, height: 1024 },
25
+ "4:3": { width: 1024, height: 768 },
26
+ "3:2": { width: 1024, height: 683 },
27
+ "16:9": { width: 1024, height: 576 },
28
+ "21:9": { width: 1344, height: 576 },
29
+ "9:16": { width: 576, height: 1024 },
30
+ };
31
+
32
+ const fail = (code, msg) => {
33
+ console.error(`[replicate] error=${code} detail=${JSON.stringify(msg)}`);
34
+ process.exit(1);
35
+ };
36
+
37
+ const args = Object.fromEntries(
38
+ process.argv.slice(2).map((a) => {
39
+ const m = a.match(/^--([^=]+)=(.*)$/);
40
+ return m ? [m[1], m[2]] : [a, true];
41
+ }),
42
+ );
43
+ const prompt = args.prompt;
44
+ const model = args.model || "nano-banana-pro";
45
+ const aspectRatio = args.aspect || "1:1";
46
+ const outputFormat = args.format || "png";
47
+ if (!prompt || prompt === true) {
48
+ fail("usage", "usage: replicate-image.mjs --prompt=<text> [--model=] [--aspect=] [--format=]");
49
+ }
50
+
51
+ const token =
52
+ process.env.REPLICATE_API_TOKEN ||
53
+ (existsSync(join(homedir(), ".replicate", "api-token"))
54
+ ? readFileSync(join(homedir(), ".replicate", "api-token"), "utf-8").trim()
55
+ : "");
56
+ if (!token) fail("no-token", "no Replicate token (set REPLICATE_API_TOKEN or ~/.replicate/api-token)");
57
+
58
+ const entry = MODELS[model];
59
+ if (!entry) fail("model", `unknown model: ${model} (use nano-banana-pro, recraft-v4, or flux-schnell)`);
60
+ if (outputFormat === "svg" && model !== "recraft-v4") fail("model", "svg output is only available with recraft-v4");
61
+ const name = outputFormat === "svg" && entry.svg ? entry.svg : entry.name;
62
+
63
+ const dims = ASPECT[aspectRatio] ?? ASPECT["1:1"];
64
+ const input =
65
+ model === "flux-schnell"
66
+ ? { prompt, aspect_ratio: aspectRatio, ...(outputFormat !== "svg" ? { output_format: outputFormat } : {}) }
67
+ : { prompt, width: dims.width, height: dims.height, ...(outputFormat !== "svg" ? { output_format: outputFormat } : {}) };
68
+
69
+ // Create a prediction against the model. Prefer:wait blocks until the prediction
70
+ // is terminal where the model supports it; otherwise we poll the get URL.
71
+ const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json", Prefer: "wait" };
72
+ let pred;
73
+ try {
74
+ const r = await fetch(`https://api.replicate.com/v1/models/${entry.owner}/${name}/predictions`, {
75
+ method: "POST",
76
+ headers,
77
+ body: JSON.stringify({ input }),
78
+ });
79
+ if (r.status === 401 || r.status === 403) fail("auth", "Replicate token rejected (invalid or revoked)");
80
+ if (!r.ok) fail("model", `Replicate API HTTP ${r.status}: ${(await r.text()).slice(0, 200)}`);
81
+ pred = await r.json();
82
+ } catch (err) {
83
+ fail("network", err instanceof Error ? err.message : String(err));
84
+ }
85
+
86
+ while (pred.status && pred.status !== "succeeded" && pred.status !== "failed" && pred.status !== "canceled") {
87
+ if (!pred.urls?.get) fail("model", "prediction is not terminal but carries no poll URL");
88
+ await new Promise((r) => setTimeout(r, 1500));
89
+ try {
90
+ const r = await fetch(pred.urls.get, { headers: { Authorization: `Bearer ${token}` } });
91
+ pred = await r.json();
92
+ } catch (err) {
93
+ fail("network", err instanceof Error ? err.message : String(err));
94
+ }
95
+ }
96
+ if (pred.status !== "succeeded") {
97
+ fail("model", `generation ${pred.status}: ${JSON.stringify(pred.error ?? "").slice(0, 200)}`);
98
+ }
99
+
100
+ // Replicate output is a URL string or an array of URL strings, depending on model.
101
+ const out = pred.output;
102
+ const imageUrl = Array.isArray(out)
103
+ ? out.find((u) => typeof u === "string" && u.startsWith("http"))
104
+ : typeof out === "string" && out.startsWith("http")
105
+ ? out
106
+ : null;
107
+ if (!imageUrl) fail("model", "generation returned unexpected output format");
108
+
109
+ let buf;
110
+ try {
111
+ const r = await fetch(imageUrl);
112
+ if (!r.ok) throw new Error(`HTTP ${r.status}`);
113
+ buf = Buffer.from(await r.arrayBuffer());
114
+ } catch (err) {
115
+ fail("download", err instanceof Error ? err.message : String(err));
116
+ }
117
+
118
+ const ext = outputFormat === "svg" ? "svg" : outputFormat;
119
+ const date = new Date().toISOString().slice(0, 10);
120
+ const vault = process.env.LITE_VAULT || join(homedir(), ".maxy-lite", "vault");
121
+ const genDir = resolve(vault, "Assets", "generated");
122
+ const file = resolve(genDir, `${date}-${randomUUID().slice(0, 6)}.${ext}`);
123
+ try {
124
+ mkdirSync(genDir, { recursive: true });
125
+ writeFileSync(file, buf);
126
+ } catch (err) {
127
+ fail("disk", err instanceof Error ? err.message : String(err));
128
+ }
129
+
130
+ console.error(`[replicate] complete model=${model} format=${outputFormat} aspect=${aspectRatio} file=${file}`);
131
+ process.stdout.write(file + "\n");