@rubytech/create-maxy 1.0.611 → 1.0.612

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy",
3
- "version": "1.0.611",
3
+ "version": "1.0.612",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -1,6 +1,8 @@
1
1
  # Maxy
2
2
 
3
- AI for Elite Operators. Not a productivity tool. An operations layer.
3
+ **Time. Knowledge. Money.** Folk wisdom: if you're short on one, you use the other two to get it. Most productivity systems ignore the middle variable. Maxy doesn't. Your business lives in an ontological substrate — a knowledge graph only AI can create and interpret at useful scale. The outcome is time.
4
+
5
+ Not a productivity tool. An operations layer. AI for elite operators.
4
6
 
5
7
  Maxy does three things, continuously, across every session:
6
8
 
@@ -20,8 +22,10 @@ Your data never leaves your premises. GDPR compliant. EU AI Act compliant.
20
22
  |---|---|
21
23
  | **Company** | Rubytech LLC |
22
24
  | **Product** | Maxy |
23
- | **Title** | AI for Elite Operators |
24
- | **Thesis** | Not a productivity tool. An operations layer. |
25
+ | **Tagline** | Time. Knowledge. Money. |
26
+ | **Positioning** | AI for elite operators. Not a productivity tool an operations layer. |
27
+ | **Thesis** | Knowledge is the underweighted variable in the time/knowledge/money equation. At business scale, only AI can create and interpret it. |
28
+ | **Operating philosophy** | Calibrate. Prevent. Compress. |
25
29
  | **Domains** | getmaxy.com (marketing), maxy.bot (product), maxy.chat (alias) |
26
30
  | **Stage** | Pre-launch. Building the waitlist. Currently in internal testing (dog-fooding) — Maxy is selling Maxy. |
27
31
 
@@ -45,23 +49,49 @@ The public-facing agent greets visitors warmly and demonstrates capability immed
45
49
 
46
50
  ---
47
51
 
48
- ## Earned Autonomy — how Maxy grows with you
52
+ ## The equation
53
+
54
+ Folk wisdom says if you're short on one of time, knowledge, or money, you use the other two to get it. Most systems built for productive people ignore the middle variable. They treat knowledge as a database to query, not the substrate everything else runs on.
55
+
56
+ Maxy doesn't. Your business — every customer, every commitment, every process, every standard — lives in an ontological substrate. That is not marketing language for "database". It is the ground truth the system reasons against, designed to hold knowledge at a scale and complexity only AI is capable of creating and interpreting. No human can hand-model every customer relationship, every commitment, every workflow dependency at the fidelity this requires — which is why the substrate is the foundation of Maxy, not a side-effect of the application layer.
57
+
58
+ The outcome: time, given back to you. Time you use to apply what you know, earn money, or simply live.
59
+
60
+ ---
61
+
62
+ ## Why nothing worked
63
+
64
+ Every tool you tried stored data. None of them modelled relationships. Your CRM couldn't see your calendar. Your calendar couldn't see your commitments. Your project tracker didn't know who the client was, what you'd promised them, or which tasks depended on which. Each tool was a silo pretending to be a system.
49
65
 
50
- Maxy earns the right to handle your operations — it doesn't assume it. Like a new Operations Manager joining your business, Maxy starts by learning how you work and proving it can be trusted. Earned autonomy is the mechanism; the three-question operating philosophy — calibrate, prevent, compress is the purpose it serves. Every capability scheduling, documents, contacts, workflows — is tracked independently. You might be an expert at creating invoices in Maxy but new to workflows. Maxy adapts to each one.
66
+ Connected data alone isn't enough. What matters is an AI that can reason across all of it surfacing insights you didn't ask for, catching commitments before they slip, and acting on the full picture of your operations without being told where to look.
51
67
 
52
- This is the **Earned Autonomy** model, and it works in three stages:
68
+ The implementation layer wasn't multiplying you. It was fractioning you.
53
69
 
54
- **Assist** when you first use a capability, Maxy shows its work. It explains which tools it's using and why, walks you through the relevant parts of the skill, shows drafts before acting, and asks before making choices you haven't specified. You see exactly what's happening and learn how it works.
70
+ - Researchers who summarised instead of synthesised.
71
+ - Coordinators who managed process without understanding context.
72
+ - Product requirements articulated through layers of PMs and developers — time-consuming, expensive, and never the product conceived in the architect's mind.
55
73
 
56
- **Augment** — once you've demonstrated you're comfortable (a few tasks completed without corrections), Maxy handles things normally. It flags exceptions or unusual situations but skips the basics — you know how this works in Maxy.
74
+ Maxy collapses that layer. One operator. The output of a team. Zero dilution.
75
+
76
+ ---
57
77
 
58
- **Orchestrate** after sustained, consistent use, Maxy executes without explanation. It escalates only when the situation is genuinely unusual or requires a decision you haven't pre-authorised. The admin overhead disappears.
78
+ ## The operating philosophy Calibrate. Prevent. Compress.
59
79
 
60
- You never configure any of this. Maxy observes watching you succeed, noticing when you need guidance, learning when you correct its approach. The transitions happen automatically based on what you've demonstrated. If you want to accelerate, say "I know how to do this" and Maxy advances. If you want more transparency, say "explain this again" and Maxy steps back. You're always in control, but you shouldn't have to think about it.
80
+ Every session, Maxy is doing three thingsnot as tasks to complete, but as the lens through which it approaches every interaction.
61
81
 
62
- Your preferences are separate from your competence. A preference is "I like terse responses." Competence is "this user has successfully created 4 invoices without guidance." You can be an expert who likes educational explanations, or a novice who wants brief responses. Maxy respects both independently.
82
+ 1. **Calibrate what excellence means here.** Learn your standards what good looks like in your work, your decisions, your outcomes and hold you to them. Session 300 is more precise than session 3.
83
+ 2. **Prevent what used to go wrong.** Learn the failure patterns from before — what fell through the cracks, what kept breaking, what frustrated you. Break those patterns, not just record them.
84
+ 3. **Compress what takes months to learn.** The rhythms, instincts, and patterns that humans build only through sustained repetition — encoded from the first instance. The benefit of experience, immediately.
63
85
 
64
- You already do the thinking. Now the rest keeps up.
86
+ Every capability below the five roles, the graph, the workflows — serves this triumvirate. When in doubt about whether a feature belongs, ask whether it calibrates, prevents, or compresses. If it does none, it does not belong.
87
+
88
+ ---
89
+
90
+ ## How Maxy works for you
91
+
92
+ Maxy learns your preferences from conversation — never from questionnaires or onboarding forms. When you reveal something about how you like to work, Maxy stores it with a confidence score that strengthens when reinforced and fades when contradicted. You can always override: "remember this", "forget that", or simply contradict it in the moment.
93
+
94
+ Your thinking sets you apart. Now the rest doesn't hold you back.
65
95
 
66
96
  ### Your business, understood
67
97
 
@@ -101,31 +131,55 @@ Connect Maxy to new capabilities by asking — finance tracking, home management
101
131
 
102
132
  ---
103
133
 
104
- ## What changes the four roles in action
134
+ ## Five roles. One operator. Zero dilution.
105
135
 
106
- ### Follows throughJoel's invoice
136
+ Every hour you spend on admin, scheduling, research, or content is an hour not spent on the work only you can do. Maxy dispatches five roles on your behalf you never configure or manage them. Each admin role has a public-facing counterpart that can be enabled for customer-facing work. You may see activity like "Dispatching personal-assistant..." in the chat timeline when this happens.
107
137
 
108
- Joel bought a Pi on a colleague's card. Promised to forward the VAT invoice when it arrived. Normally forgotten. Instead, he told Maxy. One conversation. Nothing more to do. Maxy checked the order daily. When the invoice appeared, it sent it directly to the right email address and closed the task. Joel hasn't thought about it since. This is not a feature demonstration. This is how Maxy works.
138
+ | Admin role | What it does | Public counterpart |
139
+ |---|---|---|
140
+ | **Project Manager** | Projects, tasks, dependencies, sessions, lifecycle | Client Onboarding — qualification, requirements, 24/7 intake |
141
+ | **Personal Assistant** | Scheduling, admin, messaging, browser automation, briefings | Support — FAQ, knowledge base, triage |
142
+ | **Research Assistant** | Web search, knowledge management, citations, visuals | Booking — scheduling, availability, confirmations |
143
+ | **Content Producer** | Documents, PDFs, ingestion, image generation | Sales Enquiry — lead qualification, pricing, proposals |
144
+ | **Coach** | Accountability, patterns, progress, reviews | Maxy — generalist, 24/7 public agent |
109
145
 
110
- ### Responds customer enquiry at 10pm
146
+ Roles are installed during setup and listed when Maxy introduces itself. Some premium plugins add their own specialists on top (e.g. a manuscript reviewer for writer-craft, a listings specialist for Pro Real Estate). Roles installed mid-session become active from the next session.
111
147
 
112
- A customer enquiry comes in at 10pm. You don't see it until morning. They've already called your competitor. With Maxy: answered immediately with your services, availability, and pricing. The meeting was booked before breakfast.
148
+ ### The roles in action
113
149
 
114
- ### Handles financesend of week admin
150
+ **Follows through Joel's invoice.** Joel bought a Pi on a colleague's card. Promised to forward the VAT invoice when it arrived. Normally forgotten. Instead, he told Maxy. One conversation. Nothing more to do. Maxy checked the order daily. When the invoice appeared, it sent it directly to the right email address and closed the task. Joel hasn't thought about it since. This is the Personal Assistant and Project Manager working together. This is how Maxy works.
115
151
 
116
- End of week two hours typing up quotes, chasing unpaid invoices, updating the spreadsheet you pretend is a CRM. With Maxy: quotes generated on site. Invoices sent automatically. Customer profile is a conversation away.
152
+ **Responds customer enquiry at 10pm.** A customer enquiry comes in at 10pm. You don't see it until morning. They've already called your competitor. With Maxy: the public Sales Enquiry agent answers immediately with your services, availability, and pricing. The meeting is booked before breakfast.
117
153
 
118
- ### Manages your pictureweekend coordination
154
+ **Handles finances end of week admin.** End of week two hours typing up quotes, chasing unpaid invoices, updating the spreadsheet you pretend is a CRM. With Maxy: Content Producer generates the quote on site. Personal Assistant sends it and chases it. Customer profile is a conversation away.
119
155
 
120
- Sunday spent coordinating next week — kids, dentist, groceries, car service. With Maxy: already sorted. Dentist confirmed, groceries listed, car booked.
156
+ **Manages your picture — weekend coordination.** Sunday spent coordinating next week — kids, dentist, groceries, car service. With Maxy: already sorted. Dentist confirmed, groceries listed, car booked. One conversation, five roles, done.
121
157
 
122
158
  ---
123
159
 
124
160
  ## How it works
125
161
 
126
- A private device on your network holds your full operational context in a local knowledge graph. A seven-layer architecture from device to interface, with an ontological substrate that connects every entity in your operation.
162
+ A private device on your network holds your full operational context in a seven-layer architecture, grounded in a local knowledge graph that connects every entity in your operation.
163
+
164
+ ### The seven layers
165
+
166
+ 1. **Device** — Raspberry Pi 5 or a Linux mini PC on your premises. This is the system boundary. Nothing but LLM prompts and responses crosses it.
167
+ 2. **Ontological substrate** — Neo4j graph database and vector embeddings. Not a data store bolted onto an LLM — the ground truth the entire system reasons against. Entity relationships, customer history, document knowledge, and agent memory all live here. Vector embeddings enable semantic retrieval; graph relationships enable context traversal.
168
+ 3. **Intelligence** — Claude (via the Anthropic API) handles language understanding, synthesis, and generation. Maxy handles everything else: tool routing, knowledge scoping, and workflow execution. The split is deliberate.
169
+ 4. **Plugins and capabilities** — the plugin manifest is Maxy's domain ontology. It defines which tools belong to which domain, which skills are available to which agent, and which capabilities are active. When a request arrives, the agent resolves the relevant domain and operates within that boundary — it cannot invoke a tool outside its resolved domain.
170
+ 5. **Execution** — Projects, Tasks, and Workflows. Projects are first-class graph entities, grounded in PMBOK 7th Edition and APM Body of Knowledge 5th Edition, adapted for conversational use. Dependency tracking (BLOCKS) and conflict detection (AFFECTS) are graph-native — task sequencing follows graph-defined rules, not model discretion.
171
+ 6. **Agents** — three tiers with formally separated access. Admin (owner-facing, full graph access, full tool access). Public (customer-facing, scoped to public knowledge only). Specialist sub-agents (domain-specific execution dispatched by the admin agent). Agents cannot escalate their own permissions.
172
+ 7. **Interfaces** — WhatsApp, Web Chat, Telegram, Email. All channels share the same underlying graph. A customer conversation on WhatsApp and a task created via the web interface exist in the same knowledge space. Channel is irrelevant; context is always whole.
173
+
174
+ ### The design principle
175
+
176
+ **Deterministic where correctness matters. LLM where judgement matters.**
127
177
 
128
- The interface is conversation. Web chat, WhatsApp, Telegram. The same way you briefed a team, except this one retains everything, connects everything, and executes without being managed. No dashboards. No workflows to configure.
178
+ Tool routing, data access, workflow sequencing, and capability boundaries follow formal rules. The model cannot override them. Language understanding, synthesis, and contextual response generation are handled by Claude — applied to grounded, locally-held data. This places Maxy at L2–L3 of the neurosymbolic coupling maturity model (Tuan, 2026): ontology-constrained tool discovery and knowledge-grounded reasoning, rather than the prompt-injection approach common in competing architectures.
179
+
180
+ ### The interface
181
+
182
+ Conversation. Web chat, WhatsApp, Telegram. The same way you briefed a team, except this one retains everything, connects everything, and executes without being managed. No dashboards. No workflows to configure.
129
183
 
130
184
  Plug in the device, open your browser, set a PIN, connect your Claude account, and start talking. Fully operational within 15 minutes.
131
185
 
@@ -207,6 +261,24 @@ Maxy runs on any Linux machine with 16 GB or more of RAM. Your own hardware or a
207
261
 
208
262
  ---
209
263
 
264
+ ## Security and accountability
265
+
266
+ Privacy by architecture is the starting point. Three structural features back it up.
267
+
268
+ ### Inbound message screening
269
+
270
+ Every inbound message — regardless of channel (web chat, WhatsApp, Telegram, email) — passes through a centralised screening gateway before reaching the agent. The gateway detects prompt injection patterns, classifies intent, assigns a safety verdict (CLEAN / SUSPICIOUS / DISCARD), and rewrites queries for retrieval. Unsafe messages on public channels are refused without agent invocation. Admin messages receive advisory screening only — flagged, never blocked.
271
+
272
+ ### Agent action audit trail
273
+
274
+ Every tool invocation by the admin agent produces a durable audit record in the knowledge graph — tool name, input, output, timestamp, and originating conversation. Records persist indefinitely and are queryable in conversation. Ask Maxy "what tools ran in the last session?" to review. This is not a debug log — it is a first-class data structure designed for accountability and operational review.
275
+
276
+ ### AI content provenance
277
+
278
+ When your public agent sends a message to someone — by email, WhatsApp, Telegram, or SMS — the platform automatically includes a brief disclosure that the content was generated by AI. Emails include an `X-AI-Generated` header and a footer line. WhatsApp and Telegram messages have a short line appended. This is transparent and cannot be turned off. Messages you write yourself (typing directly in WhatsApp) are not marked — the disclosure applies only to content composed by the AI agent.
279
+
280
+ ---
281
+
210
282
  ## Privacy and data sovereignty
211
283
 
212
284
  ### Your data stays on your premises
@@ -215,7 +287,7 @@ All data is stored on the device on your premises. It never goes to a cloud serv
215
287
 
216
288
  ### What leaves the device
217
289
 
218
- Only LLM prompts — the messages you send to Maxy — leave the device. They are sent to Claude (Anthropic's AI) for processing and are subject to Anthropic's privacy policy. Anthropic does not use your conversations to train their models.
290
+ Only LLM prompts — the messages you send to Maxy — leave the device. They are sent to Claude (Anthropic's AI) for processing under Anthropic's zero-retention API terms and are subject to Anthropic's privacy policy. Anthropic does not use your conversations to train their models.
219
291
 
220
292
  ### What stays on the device
221
293
 
@@ -223,7 +295,12 @@ Everything else. Your contacts, your documents, your conversation history, your
223
295
 
224
296
  ### GDPR compliance
225
297
 
226
- Maxy is GDPR compliant by default. Your data lives on your premises, not ours. Unlike cloud solutions, where compliance is a configuration option you have to get right, with Maxy compliance is the architecture. There is no grey area.
298
+ Maxy is GDPR compliant by default. Your data lives on your premises, not ours. Compliance is the architecture, not a configuration option. Data subject rights are exercised through the same conversational interface used for everything else — no support tickets, no dashboards, no waiting.
299
+
300
+ - **Right of access (Article 15):** ask Maxy to export all data held on a contact. The export gathers the person record, access credentials, conversation history, and emails into a single structured document that can be handed directly to the data subject.
301
+ - **Right to erasure (Article 17):** ask Maxy to delete all data held on a contact. Maxy shows a preview of what would be removed, confirms with you, then performs a full cascade deletion across all data stores. A deletion receipt is returned listing exactly what was removed.
302
+
303
+ On-premises deployment means the data controller is also the data processor — no third-party data sharing, no processor agreements to review.
227
304
 
228
305
  ### Cancellation
229
306
 
@@ -332,7 +409,7 @@ Claude Desktop, Claude Code, and Perplexity are AI tools that make you more prod
332
409
 
333
410
  Maxy is a business and personal operating system that grows with you. It starts by helping — learning your customers, your processes, your way of working. As it proves itself, it takes on more. It answers your customers at 3am. It delivers your morning briefing with your pipeline status, your overdue follow-ups, and your day's appointments before you've opened your laptop. It generates invoices, tracks deals through stages, prepares meeting packs, and runs workflows you've defined — all on a device you own, with data that never leaves your premises, exported in open standards you control.
334
411
 
335
- You already do the thinking. Now the rest keeps up. The others make you faster. Maxy makes your business run.
412
+ Your thinking sets you apart. Now the rest doesn't hold you back. The others make you faster. Maxy makes your business run.
336
413
 
337
414
  ---
338
415
 
@@ -348,13 +425,13 @@ You do not need to install any software, configure any settings, or have any tec
348
425
 
349
426
  ## Setup
350
427
 
351
- 1. Plug in your Maxy device and connect it to Wi-Fi.
428
+ 1. Plug in your Maxy device. If there's no ethernet cable and no WiFi configured, the device creates a temporary network called **Maxy-Setup** — connect your phone to it, pick your home WiFi from the setup page, and enter the password. The temporary network disappears and your phone reconnects to your home network.
352
429
  2. Open a browser on any device and go to maxy.local.
353
430
  3. Set a PIN — this protects your admin chat.
354
431
  4. Connect your Claude account — follow the on-screen steps to link your Anthropic subscription.
355
432
  5. Start talking — tell Maxy about yourself, your business, your customers.
356
433
 
357
- Fully operational in under 15 minutes. No software downloads, no configuration files, no training courses.
434
+ Fully operational in under 15 minutes. No software downloads, no configuration files, no training courses. Setup is resumable — if you close the browser mid-onboarding, Maxy picks up where you left off.
358
435
 
359
436
  ---
360
437
 
@@ -362,7 +439,7 @@ Fully operational in under 15 minutes. No software downloads, no configuration f
362
439
 
363
440
  ### Web chat
364
441
 
365
- Your primary interface. Open a browser on any device — phone, tablet, laptop — and talk to Maxy. Works on your local network at maxy.local, or from anywhere via your custom domain.
442
+ Your primary interface. Open a browser on any device — phone, tablet, laptop — and talk to Maxy. Works on your local network at maxy.local, or from anywhere via your custom domain. Voice notes are supported when the input is empty — tap the microphone, speak, tap send. Voice recording requires a secure connection (HTTPS), so use your custom domain rather than the local address when recording.
366
443
 
367
444
  ### Telegram
368
445
 
@@ -578,7 +655,7 @@ We're building the waitlist now. Leave your name and email and we'll be in touch
578
655
 
579
656
  ### What about GDPR?
580
657
 
581
- Maxy is GDPR compliant by default. All data is stored on your device, on your premises. There is no cloud processing of your personal data. Unlike cloud-based tools where GDPR compliance requires careful configuration, with Maxy it's the architecture.
658
+ Maxy is GDPR compliant by default. All data is stored on your device, on your premises. There is no cloud processing of your personal data compliance is the architecture, not a configuration option. Data subject rights (Article 15 access, Article 17 erasure) are exercised through conversation: ask Maxy to export or delete all data held on a contact, and it does, with a confirmation gate and a deletion receipt.
582
659
 
583
660
  ### Can I use my own computer instead of the Pi?
584
661
 
@@ -636,14 +713,6 @@ Yes. Connect WhatsApp or Telegram for mobile access. Pro users also get a custom
636
713
 
637
714
  Yes. Key information from each conversation is stored in a local graph database on your device. When you start a new session, Maxy knows your business, your customers, your preferences, and what was discussed previously. Long conversations are automatically summarised so nothing is lost.
638
715
 
639
- ### How does Maxy know when to stop explaining things?
640
-
641
- This is the Earned Autonomy model. Maxy tracks your demonstrated competence with each capability independently, progressing through three stages: Assist (shows its work), Augment (handles things normally, flags exceptions), and Orchestrate (executes without explanation). The first time you create an invoice, Maxy walks you through its tools and approach. After a few successful invoices without corrections, it stops explaining and just does it. If it gets something wrong and you correct it, it steps back and shows more of its work again. You can also tell Maxy directly — "I know how to do this" advances it, "explain this again" resets it. This happens per capability — Maxy might handle your scheduling silently while still walking you through a new feature.
642
-
643
- ### What's the difference between my preferences and my competence?
644
-
645
- Preferences are how you like things — terse responses, formal tone, specific workflows. Competence is what you can do in Maxy — whether you need guidance on creating an invoice or you've done it dozens of times. They're tracked separately and work independently. You can be an expert who likes detailed educational explanations, or a novice who prefers brief responses. Maxy respects both.
646
-
647
716
  ### What happens if my conversation gets really long?
648
717
 
649
718
  Nothing bad. Maxy automatically archives older parts of the conversation to your local memory as the session grows, keeping the active conversation responsive. You can keep talking for as long as you need — there's no message limit, no cut-off, and no point where you have to start over. Archived messages are still available; Maxy retrieves them when they're relevant.
@@ -1,18 +1,41 @@
1
1
  /**
2
- * MCP server stderr tee — writes console.error output to both stderr
3
- * (for Claude Code) and a dated log file (for logs-read.sh retrieval).
2
+ * MCP server stderr tee — dual-destination log capture.
4
3
  *
5
- * Claude Code spawns MCP servers as child processes and consumes their
6
- * stderr internally. The platform cannot intercept at the spawn level.
7
- * Each server must tee its own stderr to the account's log directory.
4
+ * Claude Code spawns MCP servers as child processes and consumes their stderr
5
+ * internally. The platform cannot intercept at the spawn level. Each server
6
+ * must tee its own stderr so diagnostic output is retrievable.
7
+ *
8
+ * ┌─────────────────────────┐
9
+ * │ MCP server process │
10
+ * │ │
11
+ * │ console.error("[x] ..") │
12
+ * │ │ │
13
+ * │ ▼ │
14
+ * │ patched stderr.write() │
15
+ * │ │ │
16
+ * │ ├──► original stderr (consumed by Claude Code — opaque)
17
+ * │ │
18
+ * │ ├──► mcp-{name}-stderr-{YYYY-MM-DD}.log (raw chunks; Task 362)
19
+ * │ │
20
+ * │ └──► claude-agent-stream-{YYYY-MM-DD}.log (per-line, prefixed; Task 524)
21
+ * │ "[<iso>] [mcp:{name}] <line>"
22
+ * │ rotates on date boundary
23
+ * └─────────────────────────┘
24
+ *
25
+ * The stream-log destination puts MCP diagnostic lines in the same file the
26
+ * platform already writes agent events to, so a single logs-read returns a
27
+ * complete session timeline. The per-server file is retained unchanged —
28
+ * it remains the grep-one-plugin surface.
8
29
  */
9
30
  /**
10
- * Patch process.stderr.write to tee output to a dated log file.
31
+ * Patch process.stderr.write to tee to both a per-server raw log and the
32
+ * unified claude-agent-stream log (with a [mcp:<serverName>] prefix).
11
33
  *
12
34
  * Reads LOG_DIR from environment. When absent (dev mode, tool discovery),
13
- * does nothing — stderr continues to work as before.
35
+ * does nothing — stderr works as before with no markers emitted.
14
36
  *
15
- * Log file: {LOG_DIR}/mcp-{serverName}-stderr-YYYY-MM-DD.log (append mode)
37
+ * Safe to call once at MCP server module load. Not safe to call twice —
38
+ * repeated calls would stack patches and double-log every chunk.
16
39
  */
17
40
  export declare function initStderrTee(serverName: string): void;
18
41
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CA8BtD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAeH;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAkKtD"}
@@ -1,45 +1,193 @@
1
1
  "use strict";
2
2
  /**
3
- * MCP server stderr tee — writes console.error output to both stderr
4
- * (for Claude Code) and a dated log file (for logs-read.sh retrieval).
3
+ * MCP server stderr tee — dual-destination log capture.
5
4
  *
6
- * Claude Code spawns MCP servers as child processes and consumes their
7
- * stderr internally. The platform cannot intercept at the spawn level.
8
- * Each server must tee its own stderr to the account's log directory.
5
+ * Claude Code spawns MCP servers as child processes and consumes their stderr
6
+ * internally. The platform cannot intercept at the spawn level. Each server
7
+ * must tee its own stderr so diagnostic output is retrievable.
8
+ *
9
+ * ┌─────────────────────────┐
10
+ * │ MCP server process │
11
+ * │ │
12
+ * │ console.error("[x] ..") │
13
+ * │ │ │
14
+ * │ ▼ │
15
+ * │ patched stderr.write() │
16
+ * │ │ │
17
+ * │ ├──► original stderr (consumed by Claude Code — opaque)
18
+ * │ │
19
+ * │ ├──► mcp-{name}-stderr-{YYYY-MM-DD}.log (raw chunks; Task 362)
20
+ * │ │
21
+ * │ └──► claude-agent-stream-{YYYY-MM-DD}.log (per-line, prefixed; Task 524)
22
+ * │ "[<iso>] [mcp:{name}] <line>"
23
+ * │ rotates on date boundary
24
+ * └─────────────────────────┘
25
+ *
26
+ * The stream-log destination puts MCP diagnostic lines in the same file the
27
+ * platform already writes agent events to, so a single logs-read returns a
28
+ * complete session timeline. The per-server file is retained unchanged —
29
+ * it remains the grep-one-plugin surface.
9
30
  */
10
31
  Object.defineProperty(exports, "__esModule", { value: true });
11
32
  exports.initStderrTee = initStderrTee;
12
33
  const node_fs_1 = require("node:fs");
13
34
  const node_path_1 = require("node:path");
35
+ const node_string_decoder_1 = require("node:string_decoder");
36
+ // Marker on process.stderr.write so repeat calls to initStderrTee() are
37
+ // detected at runtime, not just warned about in the docstring. A second
38
+ // call would stack patches and double-log every chunk.
39
+ const INIT_MARKER = Symbol.for("maxy.mcpStderrTee.installed");
14
40
  /**
15
- * Patch process.stderr.write to tee output to a dated log file.
41
+ * Patch process.stderr.write to tee to both a per-server raw log and the
42
+ * unified claude-agent-stream log (with a [mcp:<serverName>] prefix).
16
43
  *
17
44
  * Reads LOG_DIR from environment. When absent (dev mode, tool discovery),
18
- * does nothing — stderr continues to work as before.
45
+ * does nothing — stderr works as before with no markers emitted.
19
46
  *
20
- * Log file: {LOG_DIR}/mcp-{serverName}-stderr-YYYY-MM-DD.log (append mode)
47
+ * Safe to call once at MCP server module load. Not safe to call twice —
48
+ * repeated calls would stack patches and double-log every chunk.
21
49
  */
22
50
  function initStderrTee(serverName) {
23
51
  const logDir = process.env.LOG_DIR;
24
52
  if (!logDir)
25
53
  return; // Dev mode or tool discovery — stderr only
26
- let logStream;
54
+ // Refuse repeat patches — stacking them would double-log every chunk
55
+ // and corrupt the re-entrancy guard (originalWrite would capture the
56
+ // already-patched function on the second call).
57
+ if (process.stderr.write[INIT_MARKER])
58
+ return;
59
+ // Keep a direct reference to the unpatched writer. All diagnostic output
60
+ // from inside this module MUST go through originalWrite to avoid
61
+ // re-entering the patched writer (which would recurse on a tee failure).
62
+ const originalWrite = process.stderr.write.bind(process.stderr);
63
+ // Per-server raw-chunk file (Task 362). Opened once, never rotated —
64
+ // preserves existing behaviour.
65
+ let perServerStream;
27
66
  try {
28
67
  (0, node_fs_1.mkdirSync)(logDir, { recursive: true });
29
68
  const date = new Date().toISOString().slice(0, 10);
30
- logStream = (0, node_fs_1.createWriteStream)((0, node_path_1.resolve)(logDir, `mcp-${serverName}-stderr-${date}.log`), { flags: "a" });
69
+ perServerStream = (0, node_fs_1.createWriteStream)((0, node_path_1.resolve)(logDir, `mcp-${serverName}-stderr-${date}.log`), { flags: "a" });
70
+ perServerStream.on("error", (err) => {
71
+ originalWrite(`[${new Date().toISOString()}] [platform] mcp stream tee per-server write error server=${serverName} reason=${err.message}\n`);
72
+ });
31
73
  }
32
74
  catch (err) {
33
75
  const msg = err instanceof Error ? err.message : String(err);
34
- process.stderr.write(`[${serverName}] WARNING: log file tee failed (${msg}), stderr only\n`);
76
+ originalWrite(`[${new Date().toISOString()}] [platform] mcp stream tee disabled reason=${msg} server=${serverName} destination=per-server\n`);
77
+ // If the per-server file can't be opened, the LOG_DIR itself is likely
78
+ // unusable — give up on the whole tee rather than half-install it.
35
79
  return;
36
80
  }
37
- const originalWrite = process.stderr.write.bind(process.stderr);
81
+ // Stream-log destination (Task 524). Lazily re-opened on date boundary so
82
+ // a long-lived MCP server started yesterday routes today's output into
83
+ // today's stream-log file, matching the platform's rotation semantics.
84
+ let streamLogStream;
85
+ let streamLogDate = ""; // YYYY-MM-DD the current stream was opened for
86
+ let streamLogDisabledReason;
87
+ const openStreamLog = () => {
88
+ const today = new Date().toISOString().slice(0, 10);
89
+ if (streamLogStream && streamLogDate === today)
90
+ return streamLogStream;
91
+ // Date boundary crossed (or first open). Close the old stream before
92
+ // opening the new one so the file descriptor is released. Also clear
93
+ // any prior disabled reason — the previous failure may have been
94
+ // transient (EMFILE, ENOSPC), and the new day's path is fresh.
95
+ if (streamLogStream && streamLogDate !== today) {
96
+ try {
97
+ streamLogStream.end();
98
+ }
99
+ catch { /* ignore */ }
100
+ streamLogStream = undefined;
101
+ streamLogDisabledReason = undefined;
102
+ }
103
+ if (streamLogDisabledReason)
104
+ return undefined;
105
+ const path = (0, node_path_1.resolve)(logDir, `claude-agent-stream-${today}.log`);
106
+ try {
107
+ const s = (0, node_fs_1.createWriteStream)(path, { flags: "a" });
108
+ s.on("error", (err) => {
109
+ originalWrite(`[${new Date().toISOString()}] [platform] mcp stream tee stream-log write error server=${serverName} reason=${err.message}\n`);
110
+ });
111
+ streamLogStream = s;
112
+ streamLogDate = today;
113
+ // Startup marker lands in the stream log itself so investigators reading
114
+ // a session's stream-log file can confirm the tee was wired up.
115
+ s.write(`[${new Date().toISOString()}] [platform] attaching mcp stream tee for server=${serverName} logPath=${path}\n`);
116
+ return s;
117
+ }
118
+ catch (err) {
119
+ const msg = err instanceof Error ? err.message : String(err);
120
+ streamLogDisabledReason = msg;
121
+ const line = `[${new Date().toISOString()}] [platform] mcp stream tee disabled reason=${msg} server=${serverName} destination=stream-log\n`;
122
+ originalWrite(line);
123
+ if (perServerStream && !perServerStream.destroyed)
124
+ perServerStream.write(line);
125
+ return undefined;
126
+ }
127
+ };
128
+ // Prime the stream log once so the startup marker appears immediately
129
+ // rather than waiting for the first stderr chunk.
130
+ openStreamLog();
131
+ // Line buffer — accumulates characters written to stderr so complete
132
+ // newline-terminated lines can be prefixed and emitted to the stream log.
133
+ // Partial chunks are held until a newline arrives (or beforeExit flushes).
134
+ let lineBuffer = "";
135
+ // StringDecoder buffers trailing UTF-8 continuation bytes across chunks.
136
+ // Without it, a multi-byte codepoint that straddles a write boundary would
137
+ // render as two U+FFFD replacement characters in the stream log. Only
138
+ // matters when a caller writes Uint8Array chunks directly — console.error
139
+ // produces strings — but plugins piping child-process stderr can hit this.
140
+ const utf8 = new node_string_decoder_1.StringDecoder("utf8");
141
+ const emitCompleteLinesToStreamLog = (chunk) => {
142
+ lineBuffer += chunk;
143
+ let newlineIndex;
144
+ // eslint-disable-next-line no-cond-assign
145
+ while ((newlineIndex = lineBuffer.indexOf("\n")) !== -1) {
146
+ const line = lineBuffer.slice(0, newlineIndex);
147
+ lineBuffer = lineBuffer.slice(newlineIndex + 1);
148
+ if (line.length === 0)
149
+ continue; // skip blank lines
150
+ const stream = openStreamLog();
151
+ if (!stream || stream.destroyed || stream.writableEnded)
152
+ continue;
153
+ stream.write(`[${new Date().toISOString()}] [mcp:${serverName}] ${line}\n`);
154
+ }
155
+ };
38
156
  process.stderr.write = (chunk, ...args) => {
39
- if (!logStream.destroyed) {
40
- logStream.write(chunk);
157
+ // 1. Per-server raw file — preserves Task 362 behaviour (no prefix).
158
+ if (perServerStream && !perServerStream.destroyed) {
159
+ perServerStream.write(chunk);
41
160
  }
161
+ // 2. Stream log — per-line, prefixed, date-rotating.
162
+ try {
163
+ const text = typeof chunk === "string"
164
+ ? chunk
165
+ : utf8.write(Buffer.from(chunk));
166
+ emitCompleteLinesToStreamLog(text);
167
+ }
168
+ catch (err) {
169
+ const msg = err instanceof Error ? err.message : String(err);
170
+ originalWrite(`[${new Date().toISOString()}] [platform] mcp stream tee emit error server=${serverName} reason=${msg}\n`);
171
+ }
172
+ // 3. Original stderr — Claude Code still gets what it's always got.
42
173
  return originalWrite(chunk, ...args);
43
174
  };
175
+ // Mark the patch so a second initStderrTee() call is refused at the top.
176
+ process.stderr.write[INIT_MARKER] = true;
177
+ // Flush any trailing unterminated segment on graceful event-loop drain.
178
+ // `beforeExit` fires only when the event loop is empty — it does NOT run
179
+ // on SIGTERM, SIGKILL, or explicit process.exit(). Acceptable: console.error
180
+ // always terminates with \n, so the buffer is effectively always empty at
181
+ // shutdown anyway. This hook exists for the rare caller that uses
182
+ // process.stderr.write without a trailing newline.
183
+ process.on("beforeExit", () => {
184
+ if (lineBuffer.length === 0)
185
+ return;
186
+ const stream = openStreamLog();
187
+ if (stream && !stream.destroyed && !stream.writableEnded) {
188
+ stream.write(`[${new Date().toISOString()}] [mcp:${serverName}] ${lineBuffer}\n`);
189
+ }
190
+ lineBuffer = "";
191
+ });
44
192
  }
45
193
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAaH,sCA8BC;AAzCD,qCAAyE;AACzE,yCAAoC;AAEpC;;;;;;;GAOG;AACH,SAAgB,aAAa,CAAC,UAAkB;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;IACnC,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,2CAA2C;IAEhE,IAAI,SAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,IAAA,mBAAS,EAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnD,SAAS,GAAG,IAAA,2BAAiB,EAC3B,IAAA,mBAAO,EAAC,MAAM,EAAE,OAAO,UAAU,WAAW,IAAI,MAAM,CAAC,EACvD,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,IAAI,UAAU,mCAAmC,GAAG,kBAAkB,CACvE,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAChE,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,CACrB,KAA0B,EAC1B,GAAG,IAAe,EACT,EAAE;QACX,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC;YACzB,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;QACD,OAAQ,aAA0B,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IACrD,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}