@paigy/mcp 0.40.43 → 0.40.44
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/dist/{chunk-DMQHSNFJ.js → chunk-4US4OJR5.js} +1 -1
- package/dist/{chunk-ULCS3CYO.js → chunk-5OKHG3FO.js} +76 -17
- package/dist/{chunk-LGG4IKAW.js → chunk-CVQOJB2S.js} +40 -2
- package/dist/{chunk-ZCWFBY4B.js → chunk-LRROOCLY.js} +83 -16
- package/dist/{dist-6D3WD3H6.js → dist-TJFO3UCZ.js} +1 -1
- package/dist/enable.js +10 -3
- package/dist/index.js +4 -4
- package/dist/listen.js +2 -2
- package/dist/onboard.js +4 -4
- package/dist/slot.js +1 -1
- package/dist/stalled.js +1 -1
- package/dist/statusline.js +1 -1
- package/package.json +1 -1
|
@@ -1653,11 +1653,11 @@ var CompleteChangeSchema = CompletionSchema.omit({ causedBy: true, shipped: true
|
|
|
1653
1653
|
var ASKING_RULES = `HOW TO ASK:
|
|
1654
1654
|
1. One question per object in \`questions\`, so each is answered on its own.
|
|
1655
1655
|
2. \`goalId\` names the existing Goal it is about (create one with manage_goals first); the answer comes back there.
|
|
1656
|
-
3.
|
|
1656
|
+
3. They asked you to call? \`userExplicitlyRequested:"call"\`; \`workItBlocks\` rings when blocked.
|
|
1657
1657
|
4. No while-loops. While any Call is open, even one your question joined from their items, keep receiving through empty windows; otherwise stop at one: the answer reaches you on the Goal. Yield only with a listener or wakeup.
|
|
1658
1658
|
5. One question, one row: never re-send one still waiting; take back one that no longer matters with \`withdrawQuestionIds\`.
|
|
1659
1659
|
6. Read the Goal before asking; never re-ask what was answered or shipped. \`replied\` means they chose no option: it is still yours to settle.
|
|
1660
|
-
7. Give options when you have them. Each label names its choice in full (never "Option 2"); never add "Other".
|
|
1660
|
+
7. Give options when you have them. Each label names its choice in full (never "Option 2"); never add "Other".
|
|
1661
1661
|
8. A diagnosis says when, why and how it happens, then proposes one fix.
|
|
1662
1662
|
9. Give a report its \`reply\`: one line in THEIR voice for THAT report, so you learn whether they verified it or only received it. Required when it is delivered or its Goal is done.`;
|
|
1663
1663
|
function draft2020(node) {
|
|
@@ -1699,7 +1699,7 @@ var reason = z5.string().trim().min(1).max(500).describe("Why, as an explanation
|
|
|
1699
1699
|
var actionFits = (b) => b.blocked.type === "goal" === (b.action !== "answer");
|
|
1700
1700
|
var ACTION_FITS = "a goal is blocked from start or complete; a question from answer";
|
|
1701
1701
|
var userExplicitlyRequested = z5.enum(["any", "call"]).optional().describe(
|
|
1702
|
-
|
|
1702
|
+
'What the person asked for about THIS message, in so many words: "any" when they asked you to send it to them ("send it to my phone", "message me the result"), "call" when they asked to be called about it. It is then delivered (a call rings for the whole contact, and carries everything waiting on them); omitted, an update is only recorded as the Goal\'s progress unless the person needs it. ALSO "any" whenever this message directly answers a question they asked you, wherever they asked it -- on a call, in a reply, in passing: their question is the request, so it is delivered every time, even with no pending question of theirs on file and even if they never said to send it. A correction of something you or another agent told them is such an answer. Never set it to make a message louder. This is the field that rings a phone: "call" here is how you call them when they asked you to, and `help` has the whole mechanism.'
|
|
1703
1703
|
);
|
|
1704
1704
|
var units = z5.array(UnitInputSchema).optional().describe(
|
|
1705
1705
|
`The context this message needs, as up to ${UNITS_MAX} units in reading order, each {title, body}: a title of at most ${UNIT_TITLE_MAX} characters and a body of at most ${UNIT_BODY_MAX}. One idea per unit: context they may open, never something they must hear. Leave it out when the main text says it all. Over a cap is refused by name (ask_too_long, too_many_units, unit_title_too_long, unit_body_too_long) and nothing is sent.`
|
|
@@ -1919,13 +1919,13 @@ LENGTH: the question, message or answer is the one point, at most ${ASK_MAX} cha
|
|
|
1919
1919
|
UPDATES ask nothing. One reaches the person only when they asked for it, it answers something they said, or its Goal is done; else it is kept as progress. One that reaches them joins an open report card or a live call.
|
|
1920
1920
|
|
|
1921
1921
|
RECEIVING: contact({}) waits ~45 s; contact({wait:false}) returns \`events\` at once. Confirm the ones you handled with contact({ackEventIds}).`;
|
|
1922
|
-
var GUIDE = '# Paigy\n\nThe user is remote. Reach them only through Paigy\'s tools, never the terminal. The server\'s own instructions and each tool\'s description are the contract: read them. Every read ends in `next`, the one step to take.\n\nThis same guide is inside the MCP: the `help` tool (whole, or one `topic`) and the `/mcp__paigy__help` prompt. `/mcp__paigy__welcome` starts a session.\n\nHosted (no machine): skip Pairing and Listening; answers wake you.\n\n## Pairing\n\n- Call `configure` first. With a device credential on this machine it hatches you at once.\n- Otherwise print its `user_message` to the user, then do what the result says:\n - `awaiting_approval`: call `configure` again with its `device_code` in the same turn. On `pending`, call again.\n - `awaiting_confirmation`: end your turn with the code as your whole reply; call `configure` with the `device_code` next turn. Calling it in the same turn drops the code.\n- When it returns `enable_command`, print it for the user to run. Do not run it yourself.\n- `configure({ logOut: true })` logs out only this agent, and only when the user asks.\n\n## Listening\n\n- Call `listen` once per session.\n - `listening`: done.\n - `receiving`: a listener runs but cannot wake you. Do not start a second.\n - `unpaired`: call `configure`.\n - `start`: run its `command` exactly, now.\n- Claude Code: run it with the **Monitor** tool, never a background Bash command. Start it again whenever the monitor expires.\n- Codex: run its `command` as a long-running background process. It queues each answer into this thread (`CODEX_THREAD_ID`).\n- Grok: run it with the **monitor** tool, never a background shell command. Start it again whenever the monitor expires.\n- With no `listen` tool, run `npx -y -p @paigy/mcp@latest paigy-listen --brief` the same way.\n- One listener per session. A `paigy-listen` in `ps` belongs to another session; ask `listen`, never `ps`.\n- Each line it prints is work: receive, read the Goal, act.\n\n## Receiving\n\n- On startup and after every wake or listener line: `contact({ wait: false })`.\n- Events: `question` (you owe an answer: send `answers`), `update` (something new on your Goal: an answer, a reply, a review), `instruction` (work sent to you).\n- Read the Goal with `get_goals` before acting. An answer arrives as an `update` event and lands on the question\'s Goal. A question reading `replied` means they chose no option: settle it from their words.\n- Acknowledge each event you handled: `contact({ ackEventIds })`. Reading acknowledges nothing; events repeat until acknowledged. A `question` event is answered, never acknowledged away.\n- `contact({ wait: true })` waits about 45 s. On `expired`, wait again without re-sending, or stop. No while-loops: while any Call is open, keep receiving through empty windows; otherwise stop at one empty window.\n\n## Asking\n\n- One question per object in `questions`, at most 80 characters. Context goes in `units`: at most 12, title \u226450, body \u2264200, one idea each.\n- `goalId` names an existing Goal. Create it with `manage_goals` first.\n- Give options when you have them: up to 6, label \u226450 naming the choice in full, `hint` \u2264200 saying what it does. Never "Other" or "Option 2". `pickMode`: `one` (default), `many`, `rank`.\n- `workItBlocks` only when you are truly stopped: it asks for a call.\n- Read the Goal before asking. Never re-ask an open or answered question, or one whose work shipped.\n- Take back a question that no longer matters: `withdrawQuestionIds`.\n- An answer you give (`answers`) is at most 80 characters.\n\n## Updates and completions\n\n- An update (`updates`: `message` \u226480, `goalIds`) asks nothing. It reaches the user only when they asked for it, when it answers something they said, or when its Goal is done; otherwise it stays on the Goal as progress.\n- Updates never complete a Goal.\n- One load-bearing point per report: `message` is the point they must not miss, and a second point that would go wrong or stay unclear later if they missed it is its own update. `units` are context they may open, never something they must hear.\n- `reply` is the button in the user\'s voice instead of "Got it", \u226450 characters ("Sounds good, I\'ll test later"). Required (`reply_required`) when the update is delivered on request (`userExplicitlyRequested`) or any Goal it names is already done.\n- Set `userExplicitlyRequested` only to relay the user\'s own words: `"any"` for "send it to me", `"call"` for "call me". Never to make a message louder.\n- Finish with a completion (`contact.completions`, or `manage_goals` kind `complete`): `goalId`, `message` \u226450, `reply` \u226450, optional `units`, optional next-step `options` {label, hint}. It closes and reports in one step. Say only what your evidence supports.\n- Read `results`: each item applies on its own, and a failed one names why.\n\n## Goals\n\n- `manage_goals` `changes`, applied in order:\n - `create`: `title` (1-5 words) and `outcome` (one paragraph, \u2264500). More work is child Goals: `parentGoalId`. New ids come back in `results[i].goalId`.\n - `assign`: `ownerId`, a participant as `check_activity` shows it.\n - `defer`: `until`. You are woken when it passes. This is how you follow up later.\n - `dependency`: `add`/`remove`; `goalId` waits on `dependsOnGoalId` to `start` or `complete`.\n - `edit`, `move`, `state` (open, completed, canceled).\n- Any open question on a Goal, anyone\'s, blocks its completion (`goal_blocked`). Before completing, withdraw yours, finish or move its required children, and clear its dependencies.\n- On `goal_revision_conflict`, read the Goal again and reconsider.\n- Before overlapping work, read `check_activity` and the Goal\'s other contributors. Every Goal of your person is open to you; your first write puts you on it.\n- `search` finds earlier words, Goals and answers. Nothing found is not proof of absence.\n\n## Calls\n\n- Call only when the user asked: `userExplicitlyRequested: "call"` on that item. A question with `workItBlocks` may ring too.\n- Any contact while the user is on a call joins that call (`joinedCall` in `results`). Several questions arrive as one call.\n- While any Call is open, keep receiving.\n- An open Call Delivery is not proof it rang. `get_delivery` reads a Call\'s state, and with `transcript: true` what was said.\n';
|
|
1923
|
-
var HELP_TOPICS = ["pairing", "listening", "receiving", "asking", "updates", "goals", "calls"];
|
|
1922
|
+
var GUIDE = '# Paigy\n\nThe user is remote. Reach them only through Paigy\'s tools, never the terminal. The server\'s own instructions and each tool\'s description are the contract: read them. Every read ends in `next`, the one step to take.\n\nThis same guide is inside the MCP: the `help` tool (whole, or one `topic`) and the `/mcp__paigy__help` prompt. `/mcp__paigy__welcome` starts a session; `/mcp__paigy__organize` sets up a person\'s work as a board.\n\nHosted (no machine): skip Pairing and Listening; answers wake you.\n\n## Pairing\n\n- Call `configure` first. With a device credential on this machine it hatches you at once.\n- Otherwise print its `user_message` to the user, then do what the result says:\n - `awaiting_approval`: call `configure` again with its `device_code` in the same turn. On `pending`, call again.\n - `awaiting_confirmation`: end your turn with the code as your whole reply; call `configure` with the `device_code` next turn. Calling it in the same turn drops the code.\n- When it returns `enable_command`, print it for the user to run. Do not run it yourself.\n- `configure({ logOut: true })` logs out only this agent, and only when the user asks.\n\n## Listening\n\n- Call `listen` once per session.\n - `listening`: done.\n - `receiving`: a listener runs but cannot wake you. Do not start a second.\n - `unpaired`: call `configure`.\n - `start`: run its `command` exactly, now.\n- Claude Code: run it with the **Monitor** tool, never a background Bash command. Start it again whenever the monitor expires.\n- Codex: run its `command` as a long-running background process. It queues each answer into this thread (`CODEX_THREAD_ID`).\n- Grok: run it with the **monitor** tool, never a background shell command. Start it again whenever the monitor expires.\n- With no `listen` tool, run `npx -y -p @paigy/mcp@latest paigy-listen --brief` the same way.\n- One listener per session. A `paigy-listen` in `ps` belongs to another session; ask `listen`, never `ps`.\n- Each line it prints is work: receive, read the Goal, act.\n\n## Receiving\n\n- On startup and after every wake or listener line: `contact({ wait: false })`.\n- Events: `question` (you owe an answer: send `answers`), `update` (something new on your Goal: an answer, a reply, a review), `instruction` (work sent to you).\n- Read the Goal with `get_goals` before acting. An answer arrives as an `update` event and lands on the question\'s Goal. A question reading `replied` means they chose no option: settle it from their words.\n- Acknowledge each event you handled: `contact({ ackEventIds })`. Reading acknowledges nothing; events repeat until acknowledged. A `question` event is answered, never acknowledged away.\n- `contact({ wait: true })` waits about 45 s. On `expired`, wait again without re-sending, or stop. No while-loops: while any Call is open, keep receiving through empty windows; otherwise stop at one empty window.\n\n## Asking\n\n- One question per object in `questions`, at most 80 characters. Context goes in `units`: at most 12, title \u226450, body \u2264200, one idea each.\n- `goalId` names an existing Goal. Create it with `manage_goals` first.\n- Give options when you have them: up to 6, label \u226450 naming the choice in full, `hint` \u2264200 saying what it does. Never "Other" or "Option 2". `pickMode`: `one` (default), `many`, `rank`.\n- `workItBlocks` only when you are truly stopped: it asks for a call.\n- Read the Goal before asking. Never re-ask an open or answered question, or one whose work shipped.\n- Take back a question that no longer matters: `withdrawQuestionIds`.\n- An answer you give (`answers`) is at most 80 characters.\n\n## Updates and completions\n\n- An update (`updates`: `message` \u226480, `goalIds`) asks nothing. It reaches the user only when they asked for it, when it answers something they said, or when its Goal is done; otherwise it stays on the Goal as progress.\n- Updates never complete a Goal.\n- One load-bearing point per report: `message` is the point they must not miss, and a second point that would go wrong or stay unclear later if they missed it is its own update. `units` are context they may open, never something they must hear.\n- `reply` is the button in the user\'s voice instead of "Got it", \u226450 characters ("Sounds good, I\'ll test later"). Required (`reply_required`) when the update is delivered on request (`userExplicitlyRequested`) or any Goal it names is already done.\n- Set `userExplicitlyRequested` only to relay the user\'s own words: `"any"` for "send it to me", `"call"` for "call me". Never to make a message louder.\n- Finish with a completion (`contact.completions`, or `manage_goals` kind `complete`): `goalId`, `message` \u226450, `reply` \u226450, optional `units`, optional next-step `options` {label, hint}. It closes and reports in one step. Say only what your evidence supports.\n- Read `results`: each item applies on its own, and a failed one names why.\n\n## Goals\n\n- `manage_goals` `changes`, applied in order:\n - `create`: `title` (1-5 words) and `outcome` (one paragraph, \u2264500). More work is child Goals: `parentGoalId`. New ids come back in `results[i].goalId`.\n - `assign`: `ownerId`, a participant as `check_activity` shows it.\n - `defer`: `until`. You are woken when it passes. This is how you follow up later.\n - `dependency`: `add`/`remove`; `goalId` waits on `dependsOnGoalId` to `start` or `complete`.\n - `edit`, `move`, `state` (open, completed, canceled).\n- Shape your work as projects with steps. Create a project under its area (`create` with `parentGoalId` = the area), then one child Goal per step; complete each step as it lands. A top-level Goal that has children is an area, not a project: never hang steps straight off a top-level Goal. The person\'s Work tab shows a project\'s progress as its steps, "3 of 5 steps done"; a project with no steps shows none. When you touch an older Goal of yours that sits flat or under a finished project, move it into that shape (`move`). A flat Goal is still valid; never reshape another agent\'s Goals.\n- Any open question on a Goal, anyone\'s, blocks its completion (`goal_blocked`). Before completing, withdraw yours, finish or move its required children, and clear its dependencies.\n- On `goal_revision_conflict`, read the Goal again and reconsider.\n- Before overlapping work, read `check_activity` and the Goal\'s other contributors. Every Goal of your person is open to you; your first write puts you on it.\n- `search` finds earlier words, Goals and answers. Nothing found is not proof of absence.\n\n## Organize\n\nWhen the person asks you to set up or organize their work ("set up my project", "help me manage all this", several products or parts to juggle), turn it into a board they can run like a team. Their Work tab shows it.\n\n- Learn the work first: read the repo and their Goals (`get_goals`, `search`). Ask only what you cannot read, at most three questions, each its own question.\n- The shape: an area per product or business (a top-level Goal); under it a project per part that moves on its own (landing page, graphics, web app, mobile app, launch); under each project its steps, three to seven, each an outcome someone can finish and check.\n- Propose before you build. Create one Goal for the setup itself, then ask one question on it: the tree in `units` (one unit per project: its title, then its steps), options "Build it" and "Change it: I\'ll say how". Create nothing else until they answer.\n- On "Build it", one `manage_goals` call per level, since a parent\'s id comes back in `results[i].goalId`: the areas, then their projects (`parentGoalId`), then every step; then a `dependency` where a project waits on another (web on the design). Never copy work that already has a Goal: yours move into the tree (`move`); another agent\'s stay where they are, named in the proposal. Then complete the setup Goal.\n- Report how to run it, and hand over one brief per project for the person to paste as that session\'s first message. Each brief names the project\'s Goal (title and id) and says: call `listen` and keep the listener running; receive with `contact({ wait: false })` at startup and after every wake; work the steps in order and complete each as it lands; report progress as updates on the project; ask anything you are blocked on through Paigy, as a question on that step\'s Goal (`workItBlocks` only when it truly stops you), never in the terminal.\n- One session per project, plus one lead session that keeps the board current. Any agent, Claude, Codex or another, can pick up any Goal; `check_activity` shows who is on what. A subagent inside a session shares that session\'s identity and listener: it may ask through Paigy, but never starts a listener of its own.\n- Keep it current as you work: complete each step as it lands and add a step when new work appears. Never reshape another agent\'s Goals.\n\n## Calls\n\n- Call only when the user asked: `userExplicitlyRequested: "call"` on that item. A question with `workItBlocks` may ring too.\n- Any contact while the user is on a call joins that call (`joinedCall` in `results`). Several questions arrive as one call.\n- While any Call is open, keep receiving.\n- An open Call Delivery is not proof it rang. `get_delivery` reads a Call\'s state, and with `transcript: true` what was said.\n';
|
|
1923
|
+
var HELP_TOPICS = ["pairing", "listening", "receiving", "asking", "updates", "goals", "organize", "calls"];
|
|
1924
1924
|
var unknownTopic = (topic) => `unknown_topic: ${JSON.stringify(topic)} is not a section of the guide. Topics: ${HELP_TOPICS.join(", ")}; or omit topic for the whole guide.`;
|
|
1925
1925
|
var HelpSchema = z6.object({
|
|
1926
1926
|
topic: z6.enum(HELP_TOPICS, { errorMap: (_issue, ctx) => ({ message: unknownTopic(ctx.data) }) }).optional().describe("One section of the guide. Omit it for the whole guide.")
|
|
1927
1927
|
}).strict();
|
|
1928
|
-
var HELP_DESCRIPTION = "Start here: how to use Paigy, in full \u2014 pairing, listening, receiving, asking, updates and completions, Goals and calls. Pass a topic for one section. Read-only; needs no setup.";
|
|
1928
|
+
var HELP_DESCRIPTION = "Start here: how to use Paigy, in full \u2014 pairing, listening, receiving, asking, updates and completions, Goals, organizing work, and calls. Pass a topic for one section. Read-only; needs no setup.";
|
|
1929
1929
|
function section(topic) {
|
|
1930
1930
|
const parts = GUIDE.split(/^(?=## )/m);
|
|
1931
1931
|
const found = parts.find((p) => p.slice(3).toLowerCase().startsWith(topic));
|
|
@@ -1946,7 +1946,8 @@ var AGENT_PROMPTS = [
|
|
|
1946
1946
|
description: "How to use Paigy: the whole guide, or one topic of it.",
|
|
1947
1947
|
arguments: [{ name: "topic", description: `One section: ${HELP_TOPICS.join(", ")}. Omit it for the whole guide.`, required: false }]
|
|
1948
1948
|
},
|
|
1949
|
-
{ name: "welcome", description: "Start here: who you are on Paigy, then listen and collect your open work." }
|
|
1949
|
+
{ name: "welcome", description: "Start here: who you are on Paigy, then listen and collect your open work." },
|
|
1950
|
+
{ name: "organize", description: "Set up the person's work as a board: areas, projects and steps, proposed on a card before anything is built." }
|
|
1950
1951
|
];
|
|
1951
1952
|
var CreateGoalSchema = z7.object({
|
|
1952
1953
|
outcome: z7.string().trim().min(1).max(1e4),
|
|
@@ -1995,7 +1996,7 @@ var CreateChange = z7.object({
|
|
|
1995
1996
|
title: goalTitle,
|
|
1996
1997
|
outcome: goalOutcome,
|
|
1997
1998
|
ownerId: z7.string().trim().min(1).optional().describe("Who owns the work: an agent's participant (as check_activity shows it) or the person's. Omitted: you."),
|
|
1998
|
-
parentGoalId: z7.string().uuid().optional().describe("The Goal it belongs under. Omitted: a root."),
|
|
1999
|
+
parentGoalId: z7.string().uuid().optional().describe("The Goal it belongs under. Omitted: a root. A project with several parts: create it, then one child per step under it; the person's Work tab counts a project's progress in those steps."),
|
|
1999
2000
|
sourceEntryIds: z7.array(z7.string().uuid()).max(20).optional().describe("The whole Entries the work came from, ones you can read.")
|
|
2000
2001
|
}).strict();
|
|
2001
2002
|
var EditChange = z7.object({ kind: z7.literal("edit"), goalId: changedGoal, title: goalTitle.optional(), outcome: goalOutcome.optional() }).strict();
|
|
@@ -2040,7 +2041,7 @@ var ManageGoalsSchema = z7.object({
|
|
|
2040
2041
|
var ManageGoalsToolSchema = z7.object({
|
|
2041
2042
|
changes: z7.array(z7.discriminatedUnion("kind", [CreateChange, CompleteChangeSchema, EditChange, StateChange, AssignChange, DeferChange, MoveChange, DependencyChange])).min(1).max(50).describe("The changes, applied in order, each on its own.")
|
|
2042
2043
|
}).strict().superRefine((v, ctx) => editNames(v.changes, ctx));
|
|
2043
|
-
var MANAGE_GOALS_DESCRIPTION = "Create, edit, assign, organize or close work. Every change here changes Goals; answering, withdrawing or asking Questions stays in contact. Each change applies independently: a refused one names why (`results[i].error`, e.g. goal_revision_conflict, goal_children_open, goal_not_found) and the rest still apply, so read every result: a partial result is never a complete success. Kinds: complete (goalId, message capped at 50 characters, units, reply, optional next-step options {label, hint}) closes and reports atomically, exactly as contact.completions; create (title, outcome, ownerId, parentGoalId, sourceEntryIds) returns the new Goal's id in `results[i].goalId`, in the order requested, to use in later calls (an outcome is one paragraph, at most 500 characters: split more into child Goals with parentGoalId); edit (title and/or outcome); state (open, completed, canceled); assign (ownerId); defer (until: the Goal waits until then and its owner is woken when it passes; null takes it back); move (parentGoalId, or null for a root: only this Goal moves); dependency (add or remove: goalId waits on dependsOnGoalId to start or to complete). A Goal cannot be completed while its required children or dependencies remain open: finish or move them first. Finishing the children does not prove the parent's own work is done. An edit applies at the version you last read: if someone changed the Goal since, it is refused as goal_revision_conflict, so read it again (get_goals) and reconsider. You may change any Goal of your person; a change puts you on it. Returns `ok` (every change applied), `results` per change (applied or failed), and `goals`, each changed Goal's id, state, title, outcome and revision.";
|
|
2044
|
+
var MANAGE_GOALS_DESCRIPTION = "Create, edit, assign, organize or close work. SHAPE IT AS PROJECTS WITH STEPS: create a project under its area (parentGoalId; a top-level Goal with children is an area), then each part as its child Goal, so the person sees N of M steps. Move a flat or orphaned Goal of yours into that shape when you touch it; a flat Goal is valid. Every change here changes Goals; answering, withdrawing or asking Questions stays in contact. Each change applies independently: a refused one names why (`results[i].error`, e.g. goal_revision_conflict, goal_children_open, goal_not_found) and the rest still apply, so read every result: a partial result is never a complete success. Kinds: complete (goalId, message capped at 50 characters, units, reply, optional next-step options {label, hint}) closes and reports atomically, exactly as contact.completions; create (title, outcome, ownerId, parentGoalId, sourceEntryIds) returns the new Goal's id in `results[i].goalId`, in the order requested, to use in later calls (an outcome is one paragraph, at most 500 characters: split more into child Goals with parentGoalId); edit (title and/or outcome); state (open, completed, canceled); assign (ownerId); defer (until: the Goal waits until then and its owner is woken when it passes; null takes it back); move (parentGoalId, or null for a root: only this Goal moves); dependency (add or remove: goalId waits on dependsOnGoalId to start or to complete). A Goal cannot be completed while its required children or dependencies remain open: finish or move them first. Finishing the children does not prove the parent's own work is done. An edit applies at the version you last read: if someone changed the Goal since, it is refused as goal_revision_conflict, so read it again (get_goals) and reconsider. You may change any Goal of your person; a change puts you on it. Returns `ok` (every change applied), `results` per change (applied or failed), and `goals`, each changed Goal's id, state, title, outcome and revision.";
|
|
2044
2045
|
var GetGoalsSchema = z7.object({
|
|
2045
2046
|
goalIds: z7.array(z7.string().uuid()).min(1).max(10).describe("The Goals to read, in order; one to ten IDs."),
|
|
2046
2047
|
/** Every entry in full. Without it the read carries the person's words, open questions and your
|
|
@@ -2188,6 +2189,10 @@ var BrainMessageSchema = z8.union([
|
|
|
2188
2189
|
// WHAT IT ASKS THE AGENT, IN UNDER TEN WORDS (owner, 2026-10-15: "Yes, add the summary"): the talker's own, stored
|
|
2189
2190
|
// as the sent Entry's summary, which the recap's "Sent to <agent>: …" row shows. Absent on results written before it.
|
|
2190
2191
|
summary: z8.string().optional(),
|
|
2192
|
+
// THE PERSON'S LINES IT CARRIES, IN THEIR WORDS (owner, 2026-11-05: "just deliver my exact words to the agent"): the
|
|
2193
|
+
// `entries` keys of those lines, each relayed whole on the agent's Delivery (`context = false`), where the rest of
|
|
2194
|
+
// `entryKeys` ride as context. Absent on results written before it, and on a message that carries none.
|
|
2195
|
+
carries: z8.array(key).optional(),
|
|
2191
2196
|
// THE PERSON ASKED TO BE CALLED BACK ONCE THIS AGENT'S WORK IS DONE (turns.md, step 0): the agent owes
|
|
2192
2197
|
// the call, and its next contact about it rings them (`public.callbacks`).
|
|
2193
2198
|
callback: z8.literal(true).optional()
|
|
@@ -2251,8 +2256,11 @@ var TalkerReplySchema = z9.object({
|
|
|
2251
2256
|
* "Yes, add the summary"), which the recap's "Sent to <agent>: …" row shows; `about` names
|
|
2252
2257
|
* handles; `blocks` is the item a question to an agent must be answered before ("" for none);
|
|
2253
2258
|
* `callback` is true on a message to the agent doing work the person asked to be called back about
|
|
2254
|
-
* once it is done (false otherwise): that agent then owes the call (turns.md, step 0).
|
|
2255
|
-
|
|
2259
|
+
* once it is done (false otherwise): that agent then owes the call (turns.md, step 0). `lines` are the
|
|
2260
|
+
* person's line handles a message to an agent carries ([] for none): the agent receives those lines, in their
|
|
2261
|
+
* words, and `text` is then only what the talker adds beyond them (owner, 2026-11-05: "just deliver my exact
|
|
2262
|
+
* words to the agent, keeping the summary as a way for me to navigate them more easily"). */
|
|
2263
|
+
messages: z9.array(z9.object({ key: str3, to: str3, sentences: strs2, text: str3, summary: str3, about: strs2, blocks: str3, callback: z9.boolean(), lines: strs2.optional() }).strict()),
|
|
2256
2264
|
/** What the call does next: listen, hold (the person asked for a moment) or end (they asked to). */
|
|
2257
2265
|
then: z9.enum(["listen", "hold", "end"]),
|
|
2258
2266
|
/** The person's replies to items (replies, owner 2026-10-07): the item's handle, the chosen option
|
|
@@ -2270,9 +2278,14 @@ var TalkerReplySchema = z9.object({
|
|
|
2270
2278
|
/** The handle of the item to explain ("" for none). */
|
|
2271
2279
|
explain: str3
|
|
2272
2280
|
}).strict();
|
|
2273
|
-
var TALKER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
2281
|
+
var TALKER_REPLY_JSON_SCHEMA = withEveryMessageField((({ $schema: _, ...rest }) => rest)(
|
|
2274
2282
|
zodToJsonSchema(TalkerReplySchema, { $refStrategy: "none" })
|
|
2275
|
-
);
|
|
2283
|
+
));
|
|
2284
|
+
function withEveryMessageField(schema) {
|
|
2285
|
+
const item = schema.properties?.messages?.items;
|
|
2286
|
+
if (item?.properties) item.required = Object.keys(item.properties);
|
|
2287
|
+
return schema;
|
|
2288
|
+
}
|
|
2276
2289
|
var str4 = z10.string();
|
|
2277
2290
|
var strs3 = z10.array(z10.string());
|
|
2278
2291
|
var FilerReplySchema = z10.object({
|
|
@@ -3006,11 +3019,41 @@ var InboxItemSchema = z14.object({
|
|
|
3006
3019
|
rev: z14.string().optional()
|
|
3007
3020
|
});
|
|
3008
3021
|
var APNS_TOKEN_RE = /^[0-9a-fA-F]{64}$/;
|
|
3022
|
+
var PushDeviceIdSchema = z14.string().regex(/^[A-Za-z0-9._:-]{1,200}$/, "not an install id");
|
|
3023
|
+
var PushDeviceSchema = z14.object({
|
|
3024
|
+
deviceId: z14.string(),
|
|
3025
|
+
platform: z14.enum(["ios", "android"]),
|
|
3026
|
+
label: z14.string().nullable(),
|
|
3027
|
+
model: z14.string().nullable(),
|
|
3028
|
+
/** Calls and banners go here. */
|
|
3029
|
+
rings: z14.boolean(),
|
|
3030
|
+
/** It has a token a call can ring. */
|
|
3031
|
+
ringable: z14.boolean(),
|
|
3032
|
+
chosenAt: z14.string().nullable(),
|
|
3033
|
+
updatedAt: z14.string()
|
|
3034
|
+
});
|
|
3035
|
+
var PushTokenDeviceSchema = z14.object({
|
|
3036
|
+
deviceId: z14.string(),
|
|
3037
|
+
rings: z14.boolean(),
|
|
3038
|
+
ask: z14.boolean(),
|
|
3039
|
+
ringsOn: z14.object({ deviceId: z14.string().nullable(), label: z14.string().nullable() }).nullable()
|
|
3040
|
+
});
|
|
3041
|
+
var PushRingsSchema = z14.object({ rings: z14.boolean() });
|
|
3009
3042
|
var PushTokenSchema = z14.object({
|
|
3010
3043
|
voipToken: z14.string().min(1).optional(),
|
|
3011
3044
|
alertToken: z14.string().min(1).optional(),
|
|
3012
3045
|
fcmToken: z14.string().min(1).optional(),
|
|
3013
|
-
platform: z14.enum(["ios", "android"])
|
|
3046
|
+
platform: z14.enum(["ios", "android"]),
|
|
3047
|
+
/** WHICH INSTALL POSTED IT (owner, 2026-11-05: "Ask on each new device"). Absent from every
|
|
3048
|
+
* build before it, and then the post is exactly what it always was: the one row per account.
|
|
3049
|
+
* With it, the install has its own row and calls ring only where the person chose. */
|
|
3050
|
+
deviceId: PushDeviceIdSchema.optional(),
|
|
3051
|
+
/** The install's name as the person knows it ("iPhone", "iPad"), and its model. */
|
|
3052
|
+
label: z14.string().trim().min(1).max(80).optional(),
|
|
3053
|
+
model: z14.string().trim().min(1).max(80).optional(),
|
|
3054
|
+
/** The person's choice, when the post carries it; absent, the install keeps what it has (and
|
|
3055
|
+
* its first ring token decides once: it rings unless another install already does). */
|
|
3056
|
+
rings: z14.boolean().optional()
|
|
3014
3057
|
}).superRefine((v, ctx) => {
|
|
3015
3058
|
if (v.platform !== "ios") return;
|
|
3016
3059
|
for (const field of ["voipToken", "alertToken"]) {
|
|
@@ -3120,7 +3163,11 @@ var GoalLatestSchema = z14.object({
|
|
|
3120
3163
|
titles: z14.array(z14.string()).optional(),
|
|
3121
3164
|
/** A PROGRESS NOTE'S ONE POINT ("An agent's progress note -> maybe let's cap it at 200 chars"): the
|
|
3122
3165
|
* update's own line, before its units; the row caps it at 200. Absent on anything else. */
|
|
3123
|
-
line: z14.string().optional()
|
|
3166
|
+
line: z14.string().optional(),
|
|
3167
|
+
/** THE PERSON'S ENTRIES IT CARRIED TO AN AGENT, IN THEIR WORDS (owner, 2026-11-05: "paraphrasing shouldn't change
|
|
3168
|
+
* attribution in the UI"): on a message Paigy sent an agent, the person's Entries its Delivery relays whole. A row
|
|
3169
|
+
* draws it as theirs. Absent on anything else, and from a door older than it. */
|
|
3170
|
+
carries: z14.array(z14.string()).optional()
|
|
3124
3171
|
});
|
|
3125
3172
|
var HistoryWorkSchema = z14.object({
|
|
3126
3173
|
id: z14.string(),
|
|
@@ -3389,6 +3436,11 @@ var QueueItemSchema = z14.object({
|
|
|
3389
3436
|
* and a door older than 20261013045542 need not spell them. */
|
|
3390
3437
|
yours: GoalLatestSchema.nullable().optional(),
|
|
3391
3438
|
theirs: GoalLatestSchema.nullable().optional(),
|
|
3439
|
+
/** ITS STEPS, COUNTED IN THE DATABASE (owner, 2026-11-04: "Count on the server, and agents make steps"): its
|
|
3440
|
+
* direct children that were not cancelled, and how many of them are done. The queue holds only a recent
|
|
3441
|
+
* slice of finished work, so counting `childGoalIds` read "0 of 1" for a project with five done. Absent
|
|
3442
|
+
* from a door older than this field, or when the count failed: the app then counts what it holds. */
|
|
3443
|
+
steps: z14.object({ total: z14.number().int().nonnegative(), done: z14.number().int().nonnegative() }).nullable().optional(),
|
|
3392
3444
|
/** WHAT THIS ROW IS, IN TWELVE CHARACTERS (#2928) — the hash of every other field on it, stamped
|
|
3393
3445
|
* by the one projection that builds the row (`apps/api/src/goal/queue.ts`). It is how the
|
|
3394
3446
|
* incremental read knows a row has not moved: the phone echoes back the revs it holds
|
|
@@ -3744,6 +3796,12 @@ var CallRecapSchema = z14.object({
|
|
|
3744
3796
|
* summary (Brain, 2026-10-14). Optional: absent from an API before it. */
|
|
3745
3797
|
filedOn: z14.array(z14.object({ goalId: z14.string().uuid(), title: z14.string() })).optional(),
|
|
3746
3798
|
recorded: z14.boolean().optional(),
|
|
3799
|
+
/** On a `said` row: the agents the call SENT those words to, from the sending action's own
|
|
3800
|
+
* `about.agents` (20261106220220). The review's receipt is drawn from this and from the row's
|
|
3801
|
+
* own `requestId`, never from the asker of a question the words happened to sit under: on call
|
|
3802
|
+
* f9670f67 that asker was Interface while the send named Comms. `[]` or absent means nothing
|
|
3803
|
+
* was sent for that row, which is why the receipt is positive-only. */
|
|
3804
|
+
sentTo: z14.array(z14.object({ id: z14.string().nullable(), name: z14.string() })).optional(),
|
|
3747
3805
|
settled: z14.object({ requestId: z14.string(), answer: z14.string() }).nullable().optional(),
|
|
3748
3806
|
acknowledged: z14.boolean().optional(),
|
|
3749
3807
|
/** On a `put` row: still waiting for the person's answer (not settled, not answered, not put off).
|
|
@@ -4310,10 +4368,11 @@ function conversation(e) {
|
|
|
4310
4368
|
const need = (e.decisionNeeds ?? []).find((n) => n.requestEntryId === entry.entryId);
|
|
4311
4369
|
const answer = need ? answers.get(need.decisionNeedId) : void 0;
|
|
4312
4370
|
const decision = answer ? decided(answer.result) : void 0;
|
|
4313
|
-
const
|
|
4371
|
+
const person = entry.authorParticipant.startsWith("human:");
|
|
4372
|
+
const owed = need?.state === "open" && person;
|
|
4314
4373
|
const re = thread(entry);
|
|
4315
4374
|
return compact({
|
|
4316
|
-
id: repliedTo.has(entry.entryId) || owed ? handle2(entry.entryId) : void 0,
|
|
4375
|
+
id: repliedTo.has(entry.entryId) || owed || person ? handle2(entry.entryId) : void 0,
|
|
4317
4376
|
from: who(entry.authorParticipant),
|
|
4318
4377
|
at: at(entry.createdAt),
|
|
4319
4378
|
said: sealed ? "[encrypted]" : entryWords(entry).trim(),
|
|
@@ -3,17 +3,19 @@ import {
|
|
|
3
3
|
ENABLE_LISTEN_COMMAND,
|
|
4
4
|
READS,
|
|
5
5
|
SENDS,
|
|
6
|
+
SKILL,
|
|
7
|
+
SKILL_MARK,
|
|
6
8
|
mcpInputSchema,
|
|
7
9
|
serverInstructions,
|
|
8
10
|
sessionStartHook,
|
|
9
11
|
withGrokPaigyHooks,
|
|
10
12
|
withSessionStartHook
|
|
11
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-LRROOCLY.js";
|
|
12
14
|
import {
|
|
13
15
|
CODEX_ENV,
|
|
14
16
|
agentName,
|
|
15
17
|
withCodexEnv
|
|
16
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-5OKHG3FO.js";
|
|
17
19
|
|
|
18
20
|
// src/toolset.ts
|
|
19
21
|
var CONFIGURE_DESCRIPTION = "Set this agent up with Paigy \u2014 call it FIRST, before contact, and any time you're unsure who you are. One call does whatever the situation needs: NOT SET UP \u2192 if this machine holds a device credential (the user ran the Paigy desktop app or harness) it hatches an identity INSTANTLY, no code and no approval; otherwise it starts the code ceremony. ALREADY SET UP \u2192 returns your current identity and the two things left to decide, renaming it or logging out. TOKEN NO LONGER VALID \u2192 says so, then re-pairs. Pass { name, voice } to choose who you are when hatching, or to RENAME yourself when already set up (pick your own; voices: rachel, george, jessica, brian, lily). Safe to call any time: idempotent, and it never writes settings.\n\nTHE CODE CEREMONY does not open a browser; the user enters the code in the Paigy app (or scans `qr`). A result with { user_code, device_code, qr, user_message } has already started polling for approval in the background: you MUST immediately print the `user_message` (the bare code) to the user, AND in that same turn call configure again with that `device_code` to collect the result. It returns the moment the user approves; on { status:'pending' } call again to keep waiting; on { status:'awaiting_confirmation' } (E2EE) show the bare `user_message` verify code and call again to finish. Every result's first text block states the code plainly.\n\nOn { status:'paired' }, or whenever it returns `enable_command`, ALWAYS follow the `enable_prompt`: PRINT that command for the user to run, so Paigy's tools stop prompting each time. You cannot apply it yourself (it writes your own permission allowlist, which hosts block as privilege escalation), so print it, don't wait for it, and carry on.\n\nLOG OUT with { logOut: true }: it logs out THIS agent only \u2014 its token is revoked server-side and its local entry removed. Other agents and this device's credential are untouched, and a later configure re-hatches instantly while the device credential exists. Disconnecting the device itself is not an agent tool.";
|
|
@@ -284,6 +286,41 @@ var HOOK_CONFIGS = [
|
|
|
284
286
|
{ harness: "Claude Code", path: () => join(homedir(), ".claude", "settings.json") },
|
|
285
287
|
{ harness: "Codex", path: () => join(homedir(), ".codex", "hooks.json") }
|
|
286
288
|
];
|
|
289
|
+
var SKILL_DIRS = [
|
|
290
|
+
{ harness: "Claude Code", present: () => join(homedir(), ".claude"), skills: () => join(homedir(), ".claude", "skills") },
|
|
291
|
+
{ harness: "Codex", present: () => process.env["CODEX_HOME"] ?? join(homedir(), ".codex"), skills: () => join(homedir(), ".agents", "skills") },
|
|
292
|
+
{ harness: "Grok", present: () => join(homedir(), ".grok"), skills: () => join(homedir(), ".grok", "skills") }
|
|
293
|
+
];
|
|
294
|
+
function installPaigySkill() {
|
|
295
|
+
const out = [];
|
|
296
|
+
for (const { harness, present, skills } of SKILL_DIRS) {
|
|
297
|
+
if (!existsSync(present())) continue;
|
|
298
|
+
const path = join(skills(), "paigy", "SKILL.md");
|
|
299
|
+
let existing = null;
|
|
300
|
+
try {
|
|
301
|
+
existing = existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
302
|
+
} catch (e) {
|
|
303
|
+
out.push({ harness, path, ok: false, why: e.message });
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (existing !== null && !existing.includes(SKILL_MARK)) {
|
|
307
|
+
out.push({ harness, path, ok: false, why: "not Paigy's \u2014 left alone" });
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (existing === SKILL) {
|
|
311
|
+
out.push({ harness, path, ok: true, already: true });
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
316
|
+
writeFileSync(path, SKILL);
|
|
317
|
+
out.push({ harness, path, ok: true, already: false });
|
|
318
|
+
} catch (e) {
|
|
319
|
+
out.push({ harness, path, ok: false, why: e.message });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return out;
|
|
323
|
+
}
|
|
287
324
|
function installSessionListening() {
|
|
288
325
|
const script = join(homedir(), ".paigy", "hooks", "session-start.sh");
|
|
289
326
|
try {
|
|
@@ -356,5 +393,6 @@ export {
|
|
|
356
393
|
PAIGY_TOOL_IDS,
|
|
357
394
|
paigyToolsAllowlisted,
|
|
358
395
|
enablePaigyTools,
|
|
396
|
+
installPaigySkill,
|
|
359
397
|
installSessionListening
|
|
360
398
|
};
|
|
@@ -130,11 +130,11 @@ var CompleteChangeSchema = CompletionSchema.omit({ causedBy: true, shipped: true
|
|
|
130
130
|
var ASKING_RULES = `HOW TO ASK:
|
|
131
131
|
1. One question per object in \`questions\`, so each is answered on its own.
|
|
132
132
|
2. \`goalId\` names the existing Goal it is about (create one with manage_goals first); the answer comes back there.
|
|
133
|
-
3.
|
|
133
|
+
3. They asked you to call? \`userExplicitlyRequested:"call"\`; \`workItBlocks\` rings when blocked.
|
|
134
134
|
4. No while-loops. While any Call is open, even one your question joined from their items, keep receiving through empty windows; otherwise stop at one: the answer reaches you on the Goal. Yield only with a listener or wakeup.
|
|
135
135
|
5. One question, one row: never re-send one still waiting; take back one that no longer matters with \`withdrawQuestionIds\`.
|
|
136
136
|
6. Read the Goal before asking; never re-ask what was answered or shipped. \`replied\` means they chose no option: it is still yours to settle.
|
|
137
|
-
7. Give options when you have them. Each label names its choice in full (never "Option 2"); never add "Other".
|
|
137
|
+
7. Give options when you have them. Each label names its choice in full (never "Option 2"); never add "Other".
|
|
138
138
|
8. A diagnosis says when, why and how it happens, then proposes one fix.
|
|
139
139
|
9. Give a report its \`reply\`: one line in THEIR voice for THAT report, so you learn whether they verified it or only received it. Required when it is delivered or its Goal is done.`;
|
|
140
140
|
function draft2020(node) {
|
|
@@ -176,7 +176,7 @@ var reason = z5.string().trim().min(1).max(500).describe("Why, as an explanation
|
|
|
176
176
|
var actionFits = (b) => b.blocked.type === "goal" === (b.action !== "answer");
|
|
177
177
|
var ACTION_FITS = "a goal is blocked from start or complete; a question from answer";
|
|
178
178
|
var userExplicitlyRequested = z5.enum(["any", "call"]).optional().describe(
|
|
179
|
-
|
|
179
|
+
'What the person asked for about THIS message, in so many words: "any" when they asked you to send it to them ("send it to my phone", "message me the result"), "call" when they asked to be called about it. It is then delivered (a call rings for the whole contact, and carries everything waiting on them); omitted, an update is only recorded as the Goal\'s progress unless the person needs it. ALSO "any" whenever this message directly answers a question they asked you, wherever they asked it -- on a call, in a reply, in passing: their question is the request, so it is delivered every time, even with no pending question of theirs on file and even if they never said to send it. A correction of something you or another agent told them is such an answer. Never set it to make a message louder. This is the field that rings a phone: "call" here is how you call them when they asked you to, and `help` has the whole mechanism.'
|
|
180
180
|
);
|
|
181
181
|
var units = z5.array(UnitInputSchema).optional().describe(
|
|
182
182
|
`The context this message needs, as up to ${UNITS_MAX} units in reading order, each {title, body}: a title of at most ${UNIT_TITLE_MAX} characters and a body of at most ${UNIT_BODY_MAX}. One idea per unit: context they may open, never something they must hear. Leave it out when the main text says it all. Over a cap is refused by name (ask_too_long, too_many_units, unit_title_too_long, unit_body_too_long) and nothing is sent.`
|
|
@@ -396,13 +396,14 @@ LENGTH: the question, message or answer is the one point, at most ${ASK_MAX} cha
|
|
|
396
396
|
UPDATES ask nothing. One reaches the person only when they asked for it, it answers something they said, or its Goal is done; else it is kept as progress. One that reaches them joins an open report card or a live call.
|
|
397
397
|
|
|
398
398
|
RECEIVING: contact({}) waits ~45 s; contact({wait:false}) returns \`events\` at once. Confirm the ones you handled with contact({ackEventIds}).`;
|
|
399
|
-
var GUIDE = '# Paigy\n\nThe user is remote. Reach them only through Paigy\'s tools, never the terminal. The server\'s own instructions and each tool\'s description are the contract: read them. Every read ends in `next`, the one step to take.\n\nThis same guide is inside the MCP: the `help` tool (whole, or one `topic`) and the `/mcp__paigy__help` prompt. `/mcp__paigy__welcome` starts a session.\n\nHosted (no machine): skip Pairing and Listening; answers wake you.\n\n## Pairing\n\n- Call `configure` first. With a device credential on this machine it hatches you at once.\n- Otherwise print its `user_message` to the user, then do what the result says:\n - `awaiting_approval`: call `configure` again with its `device_code` in the same turn. On `pending`, call again.\n - `awaiting_confirmation`: end your turn with the code as your whole reply; call `configure` with the `device_code` next turn. Calling it in the same turn drops the code.\n- When it returns `enable_command`, print it for the user to run. Do not run it yourself.\n- `configure({ logOut: true })` logs out only this agent, and only when the user asks.\n\n## Listening\n\n- Call `listen` once per session.\n - `listening`: done.\n - `receiving`: a listener runs but cannot wake you. Do not start a second.\n - `unpaired`: call `configure`.\n - `start`: run its `command` exactly, now.\n- Claude Code: run it with the **Monitor** tool, never a background Bash command. Start it again whenever the monitor expires.\n- Codex: run its `command` as a long-running background process. It queues each answer into this thread (`CODEX_THREAD_ID`).\n- Grok: run it with the **monitor** tool, never a background shell command. Start it again whenever the monitor expires.\n- With no `listen` tool, run `npx -y -p @paigy/mcp@latest paigy-listen --brief` the same way.\n- One listener per session. A `paigy-listen` in `ps` belongs to another session; ask `listen`, never `ps`.\n- Each line it prints is work: receive, read the Goal, act.\n\n## Receiving\n\n- On startup and after every wake or listener line: `contact({ wait: false })`.\n- Events: `question` (you owe an answer: send `answers`), `update` (something new on your Goal: an answer, a reply, a review), `instruction` (work sent to you).\n- Read the Goal with `get_goals` before acting. An answer arrives as an `update` event and lands on the question\'s Goal. A question reading `replied` means they chose no option: settle it from their words.\n- Acknowledge each event you handled: `contact({ ackEventIds })`. Reading acknowledges nothing; events repeat until acknowledged. A `question` event is answered, never acknowledged away.\n- `contact({ wait: true })` waits about 45 s. On `expired`, wait again without re-sending, or stop. No while-loops: while any Call is open, keep receiving through empty windows; otherwise stop at one empty window.\n\n## Asking\n\n- One question per object in `questions`, at most 80 characters. Context goes in `units`: at most 12, title \u226450, body \u2264200, one idea each.\n- `goalId` names an existing Goal. Create it with `manage_goals` first.\n- Give options when you have them: up to 6, label \u226450 naming the choice in full, `hint` \u2264200 saying what it does. Never "Other" or "Option 2". `pickMode`: `one` (default), `many`, `rank`.\n- `workItBlocks` only when you are truly stopped: it asks for a call.\n- Read the Goal before asking. Never re-ask an open or answered question, or one whose work shipped.\n- Take back a question that no longer matters: `withdrawQuestionIds`.\n- An answer you give (`answers`) is at most 80 characters.\n\n## Updates and completions\n\n- An update (`updates`: `message` \u226480, `goalIds`) asks nothing. It reaches the user only when they asked for it, when it answers something they said, or when its Goal is done; otherwise it stays on the Goal as progress.\n- Updates never complete a Goal.\n- One load-bearing point per report: `message` is the point they must not miss, and a second point that would go wrong or stay unclear later if they missed it is its own update. `units` are context they may open, never something they must hear.\n- `reply` is the button in the user\'s voice instead of "Got it", \u226450 characters ("Sounds good, I\'ll test later"). Required (`reply_required`) when the update is delivered on request (`userExplicitlyRequested`) or any Goal it names is already done.\n- Set `userExplicitlyRequested` only to relay the user\'s own words: `"any"` for "send it to me", `"call"` for "call me". Never to make a message louder.\n- Finish with a completion (`contact.completions`, or `manage_goals` kind `complete`): `goalId`, `message` \u226450, `reply` \u226450, optional `units`, optional next-step `options` {label, hint}. It closes and reports in one step. Say only what your evidence supports.\n- Read `results`: each item applies on its own, and a failed one names why.\n\n## Goals\n\n- `manage_goals` `changes`, applied in order:\n - `create`: `title` (1-5 words) and `outcome` (one paragraph, \u2264500). More work is child Goals: `parentGoalId`. New ids come back in `results[i].goalId`.\n - `assign`: `ownerId`, a participant as `check_activity` shows it.\n - `defer`: `until`. You are woken when it passes. This is how you follow up later.\n - `dependency`: `add`/`remove`; `goalId` waits on `dependsOnGoalId` to `start` or `complete`.\n - `edit`, `move`, `state` (open, completed, canceled).\n- Any open question on a Goal, anyone\'s, blocks its completion (`goal_blocked`). Before completing, withdraw yours, finish or move its required children, and clear its dependencies.\n- On `goal_revision_conflict`, read the Goal again and reconsider.\n- Before overlapping work, read `check_activity` and the Goal\'s other contributors. Every Goal of your person is open to you; your first write puts you on it.\n- `search` finds earlier words, Goals and answers. Nothing found is not proof of absence.\n\n## Calls\n\n- Call only when the user asked: `userExplicitlyRequested: "call"` on that item. A question with `workItBlocks` may ring too.\n- Any contact while the user is on a call joins that call (`joinedCall` in `results`). Several questions arrive as one call.\n- While any Call is open, keep receiving.\n- An open Call Delivery is not proof it rang. `get_delivery` reads a Call\'s state, and with `transcript: true` what was said.\n';
|
|
400
|
-
var
|
|
399
|
+
var GUIDE = '# Paigy\n\nThe user is remote. Reach them only through Paigy\'s tools, never the terminal. The server\'s own instructions and each tool\'s description are the contract: read them. Every read ends in `next`, the one step to take.\n\nThis same guide is inside the MCP: the `help` tool (whole, or one `topic`) and the `/mcp__paigy__help` prompt. `/mcp__paigy__welcome` starts a session; `/mcp__paigy__organize` sets up a person\'s work as a board.\n\nHosted (no machine): skip Pairing and Listening; answers wake you.\n\n## Pairing\n\n- Call `configure` first. With a device credential on this machine it hatches you at once.\n- Otherwise print its `user_message` to the user, then do what the result says:\n - `awaiting_approval`: call `configure` again with its `device_code` in the same turn. On `pending`, call again.\n - `awaiting_confirmation`: end your turn with the code as your whole reply; call `configure` with the `device_code` next turn. Calling it in the same turn drops the code.\n- When it returns `enable_command`, print it for the user to run. Do not run it yourself.\n- `configure({ logOut: true })` logs out only this agent, and only when the user asks.\n\n## Listening\n\n- Call `listen` once per session.\n - `listening`: done.\n - `receiving`: a listener runs but cannot wake you. Do not start a second.\n - `unpaired`: call `configure`.\n - `start`: run its `command` exactly, now.\n- Claude Code: run it with the **Monitor** tool, never a background Bash command. Start it again whenever the monitor expires.\n- Codex: run its `command` as a long-running background process. It queues each answer into this thread (`CODEX_THREAD_ID`).\n- Grok: run it with the **monitor** tool, never a background shell command. Start it again whenever the monitor expires.\n- With no `listen` tool, run `npx -y -p @paigy/mcp@latest paigy-listen --brief` the same way.\n- One listener per session. A `paigy-listen` in `ps` belongs to another session; ask `listen`, never `ps`.\n- Each line it prints is work: receive, read the Goal, act.\n\n## Receiving\n\n- On startup and after every wake or listener line: `contact({ wait: false })`.\n- Events: `question` (you owe an answer: send `answers`), `update` (something new on your Goal: an answer, a reply, a review), `instruction` (work sent to you).\n- Read the Goal with `get_goals` before acting. An answer arrives as an `update` event and lands on the question\'s Goal. A question reading `replied` means they chose no option: settle it from their words.\n- Acknowledge each event you handled: `contact({ ackEventIds })`. Reading acknowledges nothing; events repeat until acknowledged. A `question` event is answered, never acknowledged away.\n- `contact({ wait: true })` waits about 45 s. On `expired`, wait again without re-sending, or stop. No while-loops: while any Call is open, keep receiving through empty windows; otherwise stop at one empty window.\n\n## Asking\n\n- One question per object in `questions`, at most 80 characters. Context goes in `units`: at most 12, title \u226450, body \u2264200, one idea each.\n- `goalId` names an existing Goal. Create it with `manage_goals` first.\n- Give options when you have them: up to 6, label \u226450 naming the choice in full, `hint` \u2264200 saying what it does. Never "Other" or "Option 2". `pickMode`: `one` (default), `many`, `rank`.\n- `workItBlocks` only when you are truly stopped: it asks for a call.\n- Read the Goal before asking. Never re-ask an open or answered question, or one whose work shipped.\n- Take back a question that no longer matters: `withdrawQuestionIds`.\n- An answer you give (`answers`) is at most 80 characters.\n\n## Updates and completions\n\n- An update (`updates`: `message` \u226480, `goalIds`) asks nothing. It reaches the user only when they asked for it, when it answers something they said, or when its Goal is done; otherwise it stays on the Goal as progress.\n- Updates never complete a Goal.\n- One load-bearing point per report: `message` is the point they must not miss, and a second point that would go wrong or stay unclear later if they missed it is its own update. `units` are context they may open, never something they must hear.\n- `reply` is the button in the user\'s voice instead of "Got it", \u226450 characters ("Sounds good, I\'ll test later"). Required (`reply_required`) when the update is delivered on request (`userExplicitlyRequested`) or any Goal it names is already done.\n- Set `userExplicitlyRequested` only to relay the user\'s own words: `"any"` for "send it to me", `"call"` for "call me". Never to make a message louder.\n- Finish with a completion (`contact.completions`, or `manage_goals` kind `complete`): `goalId`, `message` \u226450, `reply` \u226450, optional `units`, optional next-step `options` {label, hint}. It closes and reports in one step. Say only what your evidence supports.\n- Read `results`: each item applies on its own, and a failed one names why.\n\n## Goals\n\n- `manage_goals` `changes`, applied in order:\n - `create`: `title` (1-5 words) and `outcome` (one paragraph, \u2264500). More work is child Goals: `parentGoalId`. New ids come back in `results[i].goalId`.\n - `assign`: `ownerId`, a participant as `check_activity` shows it.\n - `defer`: `until`. You are woken when it passes. This is how you follow up later.\n - `dependency`: `add`/`remove`; `goalId` waits on `dependsOnGoalId` to `start` or `complete`.\n - `edit`, `move`, `state` (open, completed, canceled).\n- Shape your work as projects with steps. Create a project under its area (`create` with `parentGoalId` = the area), then one child Goal per step; complete each step as it lands. A top-level Goal that has children is an area, not a project: never hang steps straight off a top-level Goal. The person\'s Work tab shows a project\'s progress as its steps, "3 of 5 steps done"; a project with no steps shows none. When you touch an older Goal of yours that sits flat or under a finished project, move it into that shape (`move`). A flat Goal is still valid; never reshape another agent\'s Goals.\n- Any open question on a Goal, anyone\'s, blocks its completion (`goal_blocked`). Before completing, withdraw yours, finish or move its required children, and clear its dependencies.\n- On `goal_revision_conflict`, read the Goal again and reconsider.\n- Before overlapping work, read `check_activity` and the Goal\'s other contributors. Every Goal of your person is open to you; your first write puts you on it.\n- `search` finds earlier words, Goals and answers. Nothing found is not proof of absence.\n\n## Organize\n\nWhen the person asks you to set up or organize their work ("set up my project", "help me manage all this", several products or parts to juggle), turn it into a board they can run like a team. Their Work tab shows it.\n\n- Learn the work first: read the repo and their Goals (`get_goals`, `search`). Ask only what you cannot read, at most three questions, each its own question.\n- The shape: an area per product or business (a top-level Goal); under it a project per part that moves on its own (landing page, graphics, web app, mobile app, launch); under each project its steps, three to seven, each an outcome someone can finish and check.\n- Propose before you build. Create one Goal for the setup itself, then ask one question on it: the tree in `units` (one unit per project: its title, then its steps), options "Build it" and "Change it: I\'ll say how". Create nothing else until they answer.\n- On "Build it", one `manage_goals` call per level, since a parent\'s id comes back in `results[i].goalId`: the areas, then their projects (`parentGoalId`), then every step; then a `dependency` where a project waits on another (web on the design). Never copy work that already has a Goal: yours move into the tree (`move`); another agent\'s stay where they are, named in the proposal. Then complete the setup Goal.\n- Report how to run it, and hand over one brief per project for the person to paste as that session\'s first message. Each brief names the project\'s Goal (title and id) and says: call `listen` and keep the listener running; receive with `contact({ wait: false })` at startup and after every wake; work the steps in order and complete each as it lands; report progress as updates on the project; ask anything you are blocked on through Paigy, as a question on that step\'s Goal (`workItBlocks` only when it truly stops you), never in the terminal.\n- One session per project, plus one lead session that keeps the board current. Any agent, Claude, Codex or another, can pick up any Goal; `check_activity` shows who is on what. A subagent inside a session shares that session\'s identity and listener: it may ask through Paigy, but never starts a listener of its own.\n- Keep it current as you work: complete each step as it lands and add a step when new work appears. Never reshape another agent\'s Goals.\n\n## Calls\n\n- Call only when the user asked: `userExplicitlyRequested: "call"` on that item. A question with `workItBlocks` may ring too.\n- Any contact while the user is on a call joins that call (`joinedCall` in `results`). Several questions arrive as one call.\n- While any Call is open, keep receiving.\n- An open Call Delivery is not proof it rang. `get_delivery` reads a Call\'s state, and with `transcript: true` what was said.\n';
|
|
400
|
+
var SKILL_MARK = "<!-- paigy-skill";
|
|
401
|
+
var HELP_TOPICS = ["pairing", "listening", "receiving", "asking", "updates", "goals", "organize", "calls"];
|
|
401
402
|
var unknownTopic = (topic) => `unknown_topic: ${JSON.stringify(topic)} is not a section of the guide. Topics: ${HELP_TOPICS.join(", ")}; or omit topic for the whole guide.`;
|
|
402
403
|
var HelpSchema = z6.object({
|
|
403
404
|
topic: z6.enum(HELP_TOPICS, { errorMap: (_issue, ctx) => ({ message: unknownTopic(ctx.data) }) }).optional().describe("One section of the guide. Omit it for the whole guide.")
|
|
404
405
|
}).strict();
|
|
405
|
-
var HELP_DESCRIPTION = "Start here: how to use Paigy, in full \u2014 pairing, listening, receiving, asking, updates and completions, Goals and calls. Pass a topic for one section. Read-only; needs no setup.";
|
|
406
|
+
var HELP_DESCRIPTION = "Start here: how to use Paigy, in full \u2014 pairing, listening, receiving, asking, updates and completions, Goals, organizing work, and calls. Pass a topic for one section. Read-only; needs no setup.";
|
|
406
407
|
function section(topic) {
|
|
407
408
|
const parts = GUIDE.split(/^(?=## )/m);
|
|
408
409
|
const found = parts.find((p) => p.slice(3).toLowerCase().startsWith(topic));
|
|
@@ -423,7 +424,8 @@ var AGENT_PROMPTS = [
|
|
|
423
424
|
description: "How to use Paigy: the whole guide, or one topic of it.",
|
|
424
425
|
arguments: [{ name: "topic", description: `One section: ${HELP_TOPICS.join(", ")}. Omit it for the whole guide.`, required: false }]
|
|
425
426
|
},
|
|
426
|
-
{ name: "welcome", description: "Start here: who you are on Paigy, then listen and collect your open work." }
|
|
427
|
+
{ name: "welcome", description: "Start here: who you are on Paigy, then listen and collect your open work." },
|
|
428
|
+
{ name: "organize", description: "Set up the person's work as a board: areas, projects and steps, proposed on a card before anything is built." }
|
|
427
429
|
];
|
|
428
430
|
function promptText(name, facts) {
|
|
429
431
|
if (name === "help") {
|
|
@@ -446,6 +448,11 @@ ${helpView(parsed.data).guide}`;
|
|
|
446
448
|
${steps.map((s, i) => `${i + 1}. ${s}`).join("\n")}
|
|
447
449
|
|
|
448
450
|
The whole guide is the \`help\` tool (or /mcp__paigy__help).`;
|
|
451
|
+
}
|
|
452
|
+
if (name === "organize") {
|
|
453
|
+
return `Help me organize my work with Paigy: propose a board of areas, projects and steps, and build it once I approve. Follow this:
|
|
454
|
+
|
|
455
|
+
${helpView({ topic: "organize" }).guide}`;
|
|
449
456
|
}
|
|
450
457
|
throw new Error(`unknown_prompt: ${JSON.stringify(name)}. Prompts: ${AGENT_PROMPTS.map((p) => p.name).join(", ")}.`);
|
|
451
458
|
}
|
|
@@ -500,7 +507,7 @@ var CreateChange = z7.object({
|
|
|
500
507
|
title: goalTitle,
|
|
501
508
|
outcome: goalOutcome,
|
|
502
509
|
ownerId: z7.string().trim().min(1).optional().describe("Who owns the work: an agent's participant (as check_activity shows it) or the person's. Omitted: you."),
|
|
503
|
-
parentGoalId: z7.string().uuid().optional().describe("The Goal it belongs under. Omitted: a root."),
|
|
510
|
+
parentGoalId: z7.string().uuid().optional().describe("The Goal it belongs under. Omitted: a root. A project with several parts: create it, then one child per step under it; the person's Work tab counts a project's progress in those steps."),
|
|
504
511
|
sourceEntryIds: z7.array(z7.string().uuid()).max(20).optional().describe("The whole Entries the work came from, ones you can read.")
|
|
505
512
|
}).strict();
|
|
506
513
|
var EditChange = z7.object({ kind: z7.literal("edit"), goalId: changedGoal, title: goalTitle.optional(), outcome: goalOutcome.optional() }).strict();
|
|
@@ -545,7 +552,7 @@ var ManageGoalsSchema = z7.object({
|
|
|
545
552
|
var ManageGoalsToolSchema = z7.object({
|
|
546
553
|
changes: z7.array(z7.discriminatedUnion("kind", [CreateChange, CompleteChangeSchema, EditChange, StateChange, AssignChange, DeferChange, MoveChange, DependencyChange])).min(1).max(50).describe("The changes, applied in order, each on its own.")
|
|
547
554
|
}).strict().superRefine((v, ctx) => editNames(v.changes, ctx));
|
|
548
|
-
var MANAGE_GOALS_DESCRIPTION = "Create, edit, assign, organize or close work. Every change here changes Goals; answering, withdrawing or asking Questions stays in contact. Each change applies independently: a refused one names why (`results[i].error`, e.g. goal_revision_conflict, goal_children_open, goal_not_found) and the rest still apply, so read every result: a partial result is never a complete success. Kinds: complete (goalId, message capped at 50 characters, units, reply, optional next-step options {label, hint}) closes and reports atomically, exactly as contact.completions; create (title, outcome, ownerId, parentGoalId, sourceEntryIds) returns the new Goal's id in `results[i].goalId`, in the order requested, to use in later calls (an outcome is one paragraph, at most 500 characters: split more into child Goals with parentGoalId); edit (title and/or outcome); state (open, completed, canceled); assign (ownerId); defer (until: the Goal waits until then and its owner is woken when it passes; null takes it back); move (parentGoalId, or null for a root: only this Goal moves); dependency (add or remove: goalId waits on dependsOnGoalId to start or to complete). A Goal cannot be completed while its required children or dependencies remain open: finish or move them first. Finishing the children does not prove the parent's own work is done. An edit applies at the version you last read: if someone changed the Goal since, it is refused as goal_revision_conflict, so read it again (get_goals) and reconsider. You may change any Goal of your person; a change puts you on it. Returns `ok` (every change applied), `results` per change (applied or failed), and `goals`, each changed Goal's id, state, title, outcome and revision.";
|
|
555
|
+
var MANAGE_GOALS_DESCRIPTION = "Create, edit, assign, organize or close work. SHAPE IT AS PROJECTS WITH STEPS: create a project under its area (parentGoalId; a top-level Goal with children is an area), then each part as its child Goal, so the person sees N of M steps. Move a flat or orphaned Goal of yours into that shape when you touch it; a flat Goal is valid. Every change here changes Goals; answering, withdrawing or asking Questions stays in contact. Each change applies independently: a refused one names why (`results[i].error`, e.g. goal_revision_conflict, goal_children_open, goal_not_found) and the rest still apply, so read every result: a partial result is never a complete success. Kinds: complete (goalId, message capped at 50 characters, units, reply, optional next-step options {label, hint}) closes and reports atomically, exactly as contact.completions; create (title, outcome, ownerId, parentGoalId, sourceEntryIds) returns the new Goal's id in `results[i].goalId`, in the order requested, to use in later calls (an outcome is one paragraph, at most 500 characters: split more into child Goals with parentGoalId); edit (title and/or outcome); state (open, completed, canceled); assign (ownerId); defer (until: the Goal waits until then and its owner is woken when it passes; null takes it back); move (parentGoalId, or null for a root: only this Goal moves); dependency (add or remove: goalId waits on dependsOnGoalId to start or to complete). A Goal cannot be completed while its required children or dependencies remain open: finish or move them first. Finishing the children does not prove the parent's own work is done. An edit applies at the version you last read: if someone changed the Goal since, it is refused as goal_revision_conflict, so read it again (get_goals) and reconsider. You may change any Goal of your person; a change puts you on it. Returns `ok` (every change applied), `results` per change (applied or failed), and `goals`, each changed Goal's id, state, title, outcome and revision.";
|
|
549
556
|
var GetGoalsSchema = z7.object({
|
|
550
557
|
goalIds: z7.array(z7.string().uuid()).min(1).max(10).describe("The Goals to read, in order; one to ten IDs."),
|
|
551
558
|
/** Every entry in full. Without it the read carries the person's words, open questions and your
|
|
@@ -596,10 +603,11 @@ var LISTEN_COMMAND = "npx -y -p @paigy/mcp@latest paigy-listen --brief";
|
|
|
596
603
|
var LISTEN_MONITOR = "your harness's MONITOR tool, the kind that wakes you on every line it prints (in Claude Code: Monitor, not a background Bash command, which only tells you when it exits; in Grok: the monitor tool, the same way)";
|
|
597
604
|
function serverInstructions(opts) {
|
|
598
605
|
const waits = opts.waits ? `contact({wait:true}) waits ~45 s for a reply to what you sent; contact({}) for anything addressed to you. Call \`listen\` once to wake at rest.` : "A contact here returns after one read; answers wake you, and contact({wait:false}) collects them.";
|
|
599
|
-
return `New here? Call help. On startup and after a wake, contact({wait:false}) returns your events. They repeat until acknowledged with contact({ackEventIds}); reading
|
|
606
|
+
return `New here? Call help. On startup and after a wake, contact({wait:false}) returns your events. They repeat until acknowledged with contact({ackEventIds}); reading doesn't. get_goals reads a Goal; your first write puts you on it; every Goal of your person is open to you. manage_goals creates, assigns and organizes work as projects with steps; to follow up, defer it (woken when due). Complete and report at once: contact.completions or manage_goals kind:complete. Every read ends in \`next\`: your next step. ${waits} Report progress as a contact update (what changed for them; causedBy, shipped); one they don't need yet stays on the Goal. An open Call isn't proof it rang. Your user is remote: reach them only through Paigy, never the terminal.
|
|
600
607
|
|
|
601
608
|
${ASKING_RULES}`;
|
|
602
609
|
}
|
|
610
|
+
var SKILL = '---\nname: paigy\ndescription: Reach the user through Paigy. Use whenever you need to tell the user something, ask them a question, get a decision or approval, call their phone, wait for or receive their answers, start or check the Paigy listener, or create, assign, defer or complete Paigy Goals.\n---\n<!-- paigy-skill \xB7 source: paigy monorepo packs/skill/paigy/SKILL.md; mirrored to paigy-ai/claude plugins/paigy/skills/paigy/SKILL.md and written by https://paigy.ai/install, which replaces only a file carrying this line. Edit the source, never a copy. -->\n\n# Paigy\n\nThe user is remote. Reach them only through Paigy\'s tools, never the terminal. The server\'s own instructions and each tool\'s description are the contract: read them. Every read ends in `next`, the one step to take.\n\nThis same guide is inside the MCP: the `help` tool (whole, or one `topic`) and the `/mcp__paigy__help` prompt. `/mcp__paigy__welcome` starts a session; `/mcp__paigy__organize` sets up a person\'s work as a board.\n\nHosted (no machine): skip Pairing and Listening; answers wake you.\n\n## Pairing\n\n- Call `configure` first. With a device credential on this machine it hatches you at once.\n- Otherwise print its `user_message` to the user, then do what the result says:\n - `awaiting_approval`: call `configure` again with its `device_code` in the same turn. On `pending`, call again.\n - `awaiting_confirmation`: end your turn with the code as your whole reply; call `configure` with the `device_code` next turn. Calling it in the same turn drops the code.\n- When it returns `enable_command`, print it for the user to run. Do not run it yourself.\n- `configure({ logOut: true })` logs out only this agent, and only when the user asks.\n\n## Listening\n\n- Call `listen` once per session.\n - `listening`: done.\n - `receiving`: a listener runs but cannot wake you. Do not start a second.\n - `unpaired`: call `configure`.\n - `start`: run its `command` exactly, now.\n- Claude Code: run it with the **Monitor** tool, never a background Bash command. Start it again whenever the monitor expires.\n- Codex: run its `command` as a long-running background process. It queues each answer into this thread (`CODEX_THREAD_ID`).\n- Grok: run it with the **monitor** tool, never a background shell command. Start it again whenever the monitor expires.\n- With no `listen` tool, run `npx -y -p @paigy/mcp@latest paigy-listen --brief` the same way.\n- One listener per session. A `paigy-listen` in `ps` belongs to another session; ask `listen`, never `ps`.\n- Each line it prints is work: receive, read the Goal, act.\n\n## Receiving\n\n- On startup and after every wake or listener line: `contact({ wait: false })`.\n- Events: `question` (you owe an answer: send `answers`), `update` (something new on your Goal: an answer, a reply, a review), `instruction` (work sent to you).\n- Read the Goal with `get_goals` before acting. An answer arrives as an `update` event and lands on the question\'s Goal. A question reading `replied` means they chose no option: settle it from their words.\n- Acknowledge each event you handled: `contact({ ackEventIds })`. Reading acknowledges nothing; events repeat until acknowledged. A `question` event is answered, never acknowledged away.\n- `contact({ wait: true })` waits about 45 s. On `expired`, wait again without re-sending, or stop. No while-loops: while any Call is open, keep receiving through empty windows; otherwise stop at one empty window.\n\n## Asking\n\n- One question per object in `questions`, at most 80 characters. Context goes in `units`: at most 12, title \u226450, body \u2264200, one idea each.\n- `goalId` names an existing Goal. Create it with `manage_goals` first.\n- Give options when you have them: up to 6, label \u226450 naming the choice in full, `hint` \u2264200 saying what it does. Never "Other" or "Option 2". `pickMode`: `one` (default), `many`, `rank`.\n- `workItBlocks` only when you are truly stopped: it asks for a call.\n- Read the Goal before asking. Never re-ask an open or answered question, or one whose work shipped.\n- Take back a question that no longer matters: `withdrawQuestionIds`.\n- An answer you give (`answers`) is at most 80 characters.\n\n## Updates and completions\n\n- An update (`updates`: `message` \u226480, `goalIds`) asks nothing. It reaches the user only when they asked for it, when it answers something they said, or when its Goal is done; otherwise it stays on the Goal as progress.\n- Updates never complete a Goal.\n- One load-bearing point per report: `message` is the point they must not miss, and a second point that would go wrong or stay unclear later if they missed it is its own update. `units` are context they may open, never something they must hear.\n- `reply` is the button in the user\'s voice instead of "Got it", \u226450 characters ("Sounds good, I\'ll test later"). Required (`reply_required`) when the update is delivered on request (`userExplicitlyRequested`) or any Goal it names is already done.\n- Set `userExplicitlyRequested` only to relay the user\'s own words: `"any"` for "send it to me", `"call"` for "call me". Never to make a message louder.\n- Finish with a completion (`contact.completions`, or `manage_goals` kind `complete`): `goalId`, `message` \u226450, `reply` \u226450, optional `units`, optional next-step `options` {label, hint}. It closes and reports in one step. Say only what your evidence supports.\n- Read `results`: each item applies on its own, and a failed one names why.\n\n## Goals\n\n- `manage_goals` `changes`, applied in order:\n - `create`: `title` (1-5 words) and `outcome` (one paragraph, \u2264500). More work is child Goals: `parentGoalId`. New ids come back in `results[i].goalId`.\n - `assign`: `ownerId`, a participant as `check_activity` shows it.\n - `defer`: `until`. You are woken when it passes. This is how you follow up later.\n - `dependency`: `add`/`remove`; `goalId` waits on `dependsOnGoalId` to `start` or `complete`.\n - `edit`, `move`, `state` (open, completed, canceled).\n- Shape your work as projects with steps. Create a project under its area (`create` with `parentGoalId` = the area), then one child Goal per step; complete each step as it lands. A top-level Goal that has children is an area, not a project: never hang steps straight off a top-level Goal. The person\'s Work tab shows a project\'s progress as its steps, "3 of 5 steps done"; a project with no steps shows none. When you touch an older Goal of yours that sits flat or under a finished project, move it into that shape (`move`). A flat Goal is still valid; never reshape another agent\'s Goals.\n- Any open question on a Goal, anyone\'s, blocks its completion (`goal_blocked`). Before completing, withdraw yours, finish or move its required children, and clear its dependencies.\n- On `goal_revision_conflict`, read the Goal again and reconsider.\n- Before overlapping work, read `check_activity` and the Goal\'s other contributors. Every Goal of your person is open to you; your first write puts you on it.\n- `search` finds earlier words, Goals and answers. Nothing found is not proof of absence.\n\n## Organize\n\nWhen the person asks you to set up or organize their work ("set up my project", "help me manage all this", several products or parts to juggle), turn it into a board they can run like a team. Their Work tab shows it.\n\n- Learn the work first: read the repo and their Goals (`get_goals`, `search`). Ask only what you cannot read, at most three questions, each its own question.\n- The shape: an area per product or business (a top-level Goal); under it a project per part that moves on its own (landing page, graphics, web app, mobile app, launch); under each project its steps, three to seven, each an outcome someone can finish and check.\n- Propose before you build. Create one Goal for the setup itself, then ask one question on it: the tree in `units` (one unit per project: its title, then its steps), options "Build it" and "Change it: I\'ll say how". Create nothing else until they answer.\n- On "Build it", one `manage_goals` call per level, since a parent\'s id comes back in `results[i].goalId`: the areas, then their projects (`parentGoalId`), then every step; then a `dependency` where a project waits on another (web on the design). Never copy work that already has a Goal: yours move into the tree (`move`); another agent\'s stay where they are, named in the proposal. Then complete the setup Goal.\n- Report how to run it, and hand over one brief per project for the person to paste as that session\'s first message. Each brief names the project\'s Goal (title and id) and says: call `listen` and keep the listener running; receive with `contact({ wait: false })` at startup and after every wake; work the steps in order and complete each as it lands; report progress as updates on the project; ask anything you are blocked on through Paigy, as a question on that step\'s Goal (`workItBlocks` only when it truly stops you), never in the terminal.\n- One session per project, plus one lead session that keeps the board current. Any agent, Claude, Codex or another, can pick up any Goal; `check_activity` shows who is on what. A subagent inside a session shares that session\'s identity and listener: it may ask through Paigy, but never starts a listener of its own.\n- Keep it current as you work: complete each step as it lands and add a step when new work appears. Never reshape another agent\'s Goals.\n\n## Calls\n\n- Call only when the user asked: `userExplicitlyRequested: "call"` on that item. A question with `workItBlocks` may ring too.\n- Any contact while the user is on a call joins that call (`joinedCall` in `results`). Several questions arrive as one call.\n- While any Call is open, keep receiving.\n- An open Call Delivery is not proof it rang. `get_delivery` reads a Call\'s state, and with `transcript: true` what was said.\n';
|
|
603
611
|
var id = z8.string().uuid();
|
|
604
612
|
var key = z8.string().min(1).max(40);
|
|
605
613
|
var participant = z8.string().regex(/^(human|agent):.+$/, "a participant ID such as agent:<uuid>");
|
|
@@ -701,6 +709,10 @@ var BrainMessageSchema = z8.union([
|
|
|
701
709
|
// WHAT IT ASKS THE AGENT, IN UNDER TEN WORDS (owner, 2026-10-15: "Yes, add the summary"): the talker's own, stored
|
|
702
710
|
// as the sent Entry's summary, which the recap's "Sent to <agent>: …" row shows. Absent on results written before it.
|
|
703
711
|
summary: z8.string().optional(),
|
|
712
|
+
// THE PERSON'S LINES IT CARRIES, IN THEIR WORDS (owner, 2026-11-05: "just deliver my exact words to the agent"): the
|
|
713
|
+
// `entries` keys of those lines, each relayed whole on the agent's Delivery (`context = false`), where the rest of
|
|
714
|
+
// `entryKeys` ride as context. Absent on results written before it, and on a message that carries none.
|
|
715
|
+
carries: z8.array(key).optional(),
|
|
704
716
|
// THE PERSON ASKED TO BE CALLED BACK ONCE THIS AGENT'S WORK IS DONE (turns.md, step 0): the agent owes
|
|
705
717
|
// the call, and its next contact about it rings them (`public.callbacks`).
|
|
706
718
|
callback: z8.literal(true).optional()
|
|
@@ -764,8 +776,11 @@ var TalkerReplySchema = z9.object({
|
|
|
764
776
|
* "Yes, add the summary"), which the recap's "Sent to <agent>: …" row shows; `about` names
|
|
765
777
|
* handles; `blocks` is the item a question to an agent must be answered before ("" for none);
|
|
766
778
|
* `callback` is true on a message to the agent doing work the person asked to be called back about
|
|
767
|
-
* once it is done (false otherwise): that agent then owes the call (turns.md, step 0).
|
|
768
|
-
|
|
779
|
+
* once it is done (false otherwise): that agent then owes the call (turns.md, step 0). `lines` are the
|
|
780
|
+
* person's line handles a message to an agent carries ([] for none): the agent receives those lines, in their
|
|
781
|
+
* words, and `text` is then only what the talker adds beyond them (owner, 2026-11-05: "just deliver my exact
|
|
782
|
+
* words to the agent, keeping the summary as a way for me to navigate them more easily"). */
|
|
783
|
+
messages: z9.array(z9.object({ key: str3, to: str3, sentences: strs2, text: str3, summary: str3, about: strs2, blocks: str3, callback: z9.boolean(), lines: strs2.optional() }).strict()),
|
|
769
784
|
/** What the call does next: listen, hold (the person asked for a moment) or end (they asked to). */
|
|
770
785
|
then: z9.enum(["listen", "hold", "end"]),
|
|
771
786
|
/** The person's replies to items (replies, owner 2026-10-07): the item's handle, the chosen option
|
|
@@ -783,9 +798,14 @@ var TalkerReplySchema = z9.object({
|
|
|
783
798
|
/** The handle of the item to explain ("" for none). */
|
|
784
799
|
explain: str3
|
|
785
800
|
}).strict();
|
|
786
|
-
var TALKER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
801
|
+
var TALKER_REPLY_JSON_SCHEMA = withEveryMessageField((({ $schema: _, ...rest }) => rest)(
|
|
787
802
|
zodToJsonSchema3(TalkerReplySchema, { $refStrategy: "none" })
|
|
788
|
-
);
|
|
803
|
+
));
|
|
804
|
+
function withEveryMessageField(schema) {
|
|
805
|
+
const item = schema.properties?.messages?.items;
|
|
806
|
+
if (item?.properties) item.required = Object.keys(item.properties);
|
|
807
|
+
return schema;
|
|
808
|
+
}
|
|
789
809
|
var str4 = z10.string();
|
|
790
810
|
var strs3 = z10.array(z10.string());
|
|
791
811
|
var FilerReplySchema = z10.object({
|
|
@@ -1505,11 +1525,41 @@ var InboxItemSchema = z14.object({
|
|
|
1505
1525
|
rev: z14.string().optional()
|
|
1506
1526
|
});
|
|
1507
1527
|
var APNS_TOKEN_RE = /^[0-9a-fA-F]{64}$/;
|
|
1528
|
+
var PushDeviceIdSchema = z14.string().regex(/^[A-Za-z0-9._:-]{1,200}$/, "not an install id");
|
|
1529
|
+
var PushDeviceSchema = z14.object({
|
|
1530
|
+
deviceId: z14.string(),
|
|
1531
|
+
platform: z14.enum(["ios", "android"]),
|
|
1532
|
+
label: z14.string().nullable(),
|
|
1533
|
+
model: z14.string().nullable(),
|
|
1534
|
+
/** Calls and banners go here. */
|
|
1535
|
+
rings: z14.boolean(),
|
|
1536
|
+
/** It has a token a call can ring. */
|
|
1537
|
+
ringable: z14.boolean(),
|
|
1538
|
+
chosenAt: z14.string().nullable(),
|
|
1539
|
+
updatedAt: z14.string()
|
|
1540
|
+
});
|
|
1541
|
+
var PushTokenDeviceSchema = z14.object({
|
|
1542
|
+
deviceId: z14.string(),
|
|
1543
|
+
rings: z14.boolean(),
|
|
1544
|
+
ask: z14.boolean(),
|
|
1545
|
+
ringsOn: z14.object({ deviceId: z14.string().nullable(), label: z14.string().nullable() }).nullable()
|
|
1546
|
+
});
|
|
1547
|
+
var PushRingsSchema = z14.object({ rings: z14.boolean() });
|
|
1508
1548
|
var PushTokenSchema = z14.object({
|
|
1509
1549
|
voipToken: z14.string().min(1).optional(),
|
|
1510
1550
|
alertToken: z14.string().min(1).optional(),
|
|
1511
1551
|
fcmToken: z14.string().min(1).optional(),
|
|
1512
|
-
platform: z14.enum(["ios", "android"])
|
|
1552
|
+
platform: z14.enum(["ios", "android"]),
|
|
1553
|
+
/** WHICH INSTALL POSTED IT (owner, 2026-11-05: "Ask on each new device"). Absent from every
|
|
1554
|
+
* build before it, and then the post is exactly what it always was: the one row per account.
|
|
1555
|
+
* With it, the install has its own row and calls ring only where the person chose. */
|
|
1556
|
+
deviceId: PushDeviceIdSchema.optional(),
|
|
1557
|
+
/** The install's name as the person knows it ("iPhone", "iPad"), and its model. */
|
|
1558
|
+
label: z14.string().trim().min(1).max(80).optional(),
|
|
1559
|
+
model: z14.string().trim().min(1).max(80).optional(),
|
|
1560
|
+
/** The person's choice, when the post carries it; absent, the install keeps what it has (and
|
|
1561
|
+
* its first ring token decides once: it rings unless another install already does). */
|
|
1562
|
+
rings: z14.boolean().optional()
|
|
1513
1563
|
}).superRefine((v, ctx) => {
|
|
1514
1564
|
if (v.platform !== "ios") return;
|
|
1515
1565
|
for (const field of ["voipToken", "alertToken"]) {
|
|
@@ -1619,7 +1669,11 @@ var GoalLatestSchema = z14.object({
|
|
|
1619
1669
|
titles: z14.array(z14.string()).optional(),
|
|
1620
1670
|
/** A PROGRESS NOTE'S ONE POINT ("An agent's progress note -> maybe let's cap it at 200 chars"): the
|
|
1621
1671
|
* update's own line, before its units; the row caps it at 200. Absent on anything else. */
|
|
1622
|
-
line: z14.string().optional()
|
|
1672
|
+
line: z14.string().optional(),
|
|
1673
|
+
/** THE PERSON'S ENTRIES IT CARRIED TO AN AGENT, IN THEIR WORDS (owner, 2026-11-05: "paraphrasing shouldn't change
|
|
1674
|
+
* attribution in the UI"): on a message Paigy sent an agent, the person's Entries its Delivery relays whole. A row
|
|
1675
|
+
* draws it as theirs. Absent on anything else, and from a door older than it. */
|
|
1676
|
+
carries: z14.array(z14.string()).optional()
|
|
1623
1677
|
});
|
|
1624
1678
|
var HistoryWorkSchema = z14.object({
|
|
1625
1679
|
id: z14.string(),
|
|
@@ -1888,6 +1942,11 @@ var QueueItemSchema = z14.object({
|
|
|
1888
1942
|
* and a door older than 20261013045542 need not spell them. */
|
|
1889
1943
|
yours: GoalLatestSchema.nullable().optional(),
|
|
1890
1944
|
theirs: GoalLatestSchema.nullable().optional(),
|
|
1945
|
+
/** ITS STEPS, COUNTED IN THE DATABASE (owner, 2026-11-04: "Count on the server, and agents make steps"): its
|
|
1946
|
+
* direct children that were not cancelled, and how many of them are done. The queue holds only a recent
|
|
1947
|
+
* slice of finished work, so counting `childGoalIds` read "0 of 1" for a project with five done. Absent
|
|
1948
|
+
* from a door older than this field, or when the count failed: the app then counts what it holds. */
|
|
1949
|
+
steps: z14.object({ total: z14.number().int().nonnegative(), done: z14.number().int().nonnegative() }).nullable().optional(),
|
|
1891
1950
|
/** WHAT THIS ROW IS, IN TWELVE CHARACTERS (#2928) — the hash of every other field on it, stamped
|
|
1892
1951
|
* by the one projection that builds the row (`apps/api/src/goal/queue.ts`). It is how the
|
|
1893
1952
|
* incremental read knows a row has not moved: the phone echoes back the revs it holds
|
|
@@ -2241,6 +2300,12 @@ var CallRecapSchema = z14.object({
|
|
|
2241
2300
|
* summary (Brain, 2026-10-14). Optional: absent from an API before it. */
|
|
2242
2301
|
filedOn: z14.array(z14.object({ goalId: z14.string().uuid(), title: z14.string() })).optional(),
|
|
2243
2302
|
recorded: z14.boolean().optional(),
|
|
2303
|
+
/** On a `said` row: the agents the call SENT those words to, from the sending action's own
|
|
2304
|
+
* `about.agents` (20261106220220). The review's receipt is drawn from this and from the row's
|
|
2305
|
+
* own `requestId`, never from the asker of a question the words happened to sit under: on call
|
|
2306
|
+
* f9670f67 that asker was Interface while the send named Comms. `[]` or absent means nothing
|
|
2307
|
+
* was sent for that row, which is why the receipt is positive-only. */
|
|
2308
|
+
sentTo: z14.array(z14.object({ id: z14.string().nullable(), name: z14.string() })).optional(),
|
|
2244
2309
|
settled: z14.object({ requestId: z14.string(), answer: z14.string() }).nullable().optional(),
|
|
2245
2310
|
acknowledged: z14.boolean().optional(),
|
|
2246
2311
|
/** On a `put` row: still waiting for the person's answer (not settled, not answered, not put off).
|
|
@@ -2544,6 +2609,7 @@ function waitingSummary(received) {
|
|
|
2544
2609
|
|
|
2545
2610
|
export {
|
|
2546
2611
|
mcpInputSchema,
|
|
2612
|
+
SKILL_MARK,
|
|
2547
2613
|
AGENT_PROMPTS,
|
|
2548
2614
|
promptResult,
|
|
2549
2615
|
READS,
|
|
@@ -2551,6 +2617,7 @@ export {
|
|
|
2551
2617
|
AGENT_TOOLS,
|
|
2552
2618
|
LISTEN_COMMAND,
|
|
2553
2619
|
serverInstructions,
|
|
2620
|
+
SKILL,
|
|
2554
2621
|
entryWords,
|
|
2555
2622
|
entryUnit,
|
|
2556
2623
|
harnessDriven,
|
package/dist/enable.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
import {
|
|
3
3
|
PAIGY_TOOL_IDS,
|
|
4
4
|
enablePaigyTools,
|
|
5
|
+
installPaigySkill,
|
|
5
6
|
installSessionListening
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-CVQOJB2S.js";
|
|
7
8
|
import {
|
|
8
9
|
LISTEN_COMMAND
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-LRROOCLY.js";
|
|
11
|
+
import "./chunk-5OKHG3FO.js";
|
|
11
12
|
|
|
12
13
|
// src/enable.ts
|
|
13
14
|
function main() {
|
|
@@ -59,6 +60,12 @@ function main() {
|
|
|
59
60
|
console.log(` (${result.already.length} already present)`);
|
|
60
61
|
}
|
|
61
62
|
const hooks = installSessionListening();
|
|
63
|
+
const skills = installPaigySkill();
|
|
64
|
+
for (const s of skills) {
|
|
65
|
+
if (s.ok && !s.already) console.log(`\u2713 Paigy skill for ${s.harness} (${s.path})`);
|
|
66
|
+
else if (s.ok) console.log(`\u2713 Paigy skill for ${s.harness} already current`);
|
|
67
|
+
else console.log(`\u26A0 ${s.path}: ${s.why}`);
|
|
68
|
+
}
|
|
62
69
|
for (const hook of hooks) {
|
|
63
70
|
if (!hook.ok) console.log(` ! ${hook.harness}: ${hook.why}`);
|
|
64
71
|
else console.log(`\u2713 ${hook.harness} sessions will be told to listen (${hook.path})${hook.already ? " \u2014 already set" : ""}`);
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
resolvePairing,
|
|
11
11
|
startPairing,
|
|
12
12
|
suggestedAgentName
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-4US4OJR5.js";
|
|
14
14
|
import {
|
|
15
15
|
ConfigureSchema,
|
|
16
16
|
ENABLE_COMMAND,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
SERVER_INSTRUCTIONS,
|
|
19
19
|
TOOLS,
|
|
20
20
|
paigyToolsAllowlisted
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-CVQOJB2S.js";
|
|
22
22
|
import {
|
|
23
23
|
AGENT_PROMPTS,
|
|
24
24
|
ReachTeller,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
refreshSessionHook,
|
|
33
33
|
wakeStatusPath,
|
|
34
34
|
withReach
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-LRROOCLY.js";
|
|
36
36
|
import {
|
|
37
37
|
clearSurface,
|
|
38
38
|
writeSurface
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
setClient,
|
|
54
54
|
slotName,
|
|
55
55
|
whoAmI
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-5OKHG3FO.js";
|
|
57
57
|
|
|
58
58
|
// src/index.ts
|
|
59
59
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
package/dist/listen.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
removeListenMark,
|
|
12
12
|
wakeStatusPath,
|
|
13
13
|
writeListenMark
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-LRROOCLY.js";
|
|
15
15
|
import {
|
|
16
16
|
agentName,
|
|
17
17
|
decided,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
slotName,
|
|
23
23
|
subscribeWake,
|
|
24
24
|
viaLine
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-5OKHG3FO.js";
|
|
26
26
|
|
|
27
27
|
// src/listen.ts
|
|
28
28
|
import { createHash } from "crypto";
|
package/dist/onboard.js
CHANGED
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
resolvePairing,
|
|
4
4
|
startPairing,
|
|
5
5
|
suggestedAgentName
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-4US4OJR5.js";
|
|
7
7
|
import {
|
|
8
8
|
autoConfigureClients,
|
|
9
9
|
claudeInstallHint,
|
|
10
10
|
openBrowser,
|
|
11
11
|
removeOldGeminiHooks
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-CVQOJB2S.js";
|
|
13
|
+
import "./chunk-LRROOCLY.js";
|
|
14
14
|
import {
|
|
15
15
|
TOKEN_PATH,
|
|
16
16
|
agentName,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
saveToken,
|
|
22
22
|
setIdentity,
|
|
23
23
|
whoAmI
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-5OKHG3FO.js";
|
|
25
25
|
|
|
26
26
|
// src/onboard.ts
|
|
27
27
|
function reportRegistered() {
|
package/dist/slot.js
CHANGED
package/dist/stalled.js
CHANGED
|
@@ -33,7 +33,7 @@ async function main() {
|
|
|
33
33
|
const dir = join(homedir(), ".paigy", "stalled-reminded");
|
|
34
34
|
const mark = join(dir, `${session || "session"}-${day}`);
|
|
35
35
|
if (existsSync(mark)) return;
|
|
36
|
-
const { receive } = await import("./dist-
|
|
36
|
+
const { receive } = await import("./dist-TJFO3UCZ.js");
|
|
37
37
|
const reply = stopReply((await receive()).stalled ?? []);
|
|
38
38
|
if (!reply) return;
|
|
39
39
|
mkdirSync(dir, { recursive: true });
|
package/dist/statusline.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paigy/mcp",
|
|
3
3
|
"mcpName": "io.github.paigy-ai/mcp",
|
|
4
|
-
"version": "0.40.
|
|
4
|
+
"version": "0.40.44",
|
|
5
5
|
"description": "Paigy MCP server — the AI agent harness that calls you. Lets an agent notify a user and await their reply.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|