@rubytech/create-maxy 1.0.3 → 1.0.5

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.3",
3
+ "version": "1.0.5",
4
4
  "description": "Install Maxy — your personal AI assistant",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -104,6 +104,13 @@ function formatElapsed(seconds: number): string {
104
104
  return s > 0 ? `${m}m ${s}s` : `${m}m`
105
105
  }
106
106
 
107
+ function formatStepElapsed(seconds: number): string {
108
+ if (seconds < 60) return `${seconds.toFixed(1)}s`
109
+ const m = Math.floor(seconds / 60)
110
+ const s = seconds % 60
111
+ return s >= 0.1 ? `${m}m ${Math.floor(s)}s` : `${m}m`
112
+ }
113
+
107
114
  // --- TimelineStep ---
108
115
 
109
116
  interface StepProps {
@@ -131,7 +138,7 @@ function TimelineStep({ icon, isPending, isError, summary, detail, elapsed, isLa
131
138
  <div className="tl-body">
132
139
  <div className="tl-row" onClick={hasDetail ? onToggle : undefined} style={{ cursor: hasDetail ? 'pointer' : 'default' }}>
133
140
  <span className="tl-summary">{summary}</span>
134
- <span className="tl-step-elapsed">{formatElapsed(elapsed)}</span>
141
+ <span className="tl-step-elapsed">{formatStepElapsed(elapsed)}</span>
135
142
  {hasDetail && (
136
143
  <span className="tl-chevron">
137
144
  {expanded ? <ChevronDown size={10} /> : <ChevronRight size={10} />}
@@ -166,7 +173,7 @@ export function ActivityTimeline({ events, isStreaming, elapsedSeconds }: Activi
166
173
 
167
174
  useEffect(() => {
168
175
  if (!isStreaming) return
169
- const id = setInterval(() => setNowMs(Date.now()), 1000)
176
+ const id = setInterval(() => setNowMs(Date.now()), 100)
170
177
  return () => clearInterval(id)
171
178
  }, [isStreaming])
172
179
 
@@ -181,7 +188,7 @@ export function ActivityTimeline({ events, isStreaming, elapsedSeconds }: Activi
181
188
  const stepElapsed = (startIdx: number, endIdx?: number): number => {
182
189
  const start = arrivalRef.current.get(startIdx) ?? nowMs
183
190
  const end = endIdx !== undefined ? (arrivalRef.current.get(endIdx) ?? nowMs) : nowMs
184
- return Math.max(0, Math.floor((end - start) / 1000))
191
+ return Math.max(0, (end - start) / 1000)
185
192
  }
186
193
 
187
194
  const toolPairs = buildToolPairs(events)
@@ -238,7 +245,7 @@ export function ActivityTimeline({ events, isStreaming, elapsedSeconds }: Activi
238
245
  : <span className="tl-summary tl-thinking-label">{e.content.slice(0, 80)}{e.content.length > 80 ? '…' : ''}</span>
239
246
  }
240
247
  </div>
241
- <span className="tl-step-elapsed">{formatElapsed(stepElapsed(i, nextIdx))}</span>
248
+ <span className="tl-step-elapsed">{formatStepElapsed(stepElapsed(i, nextIdx))}</span>
242
249
  <span className="tl-chevron">
243
250
  {isExpanded ? <ChevronDown size={10} /> : <ChevronRight size={10} />}
244
251
  </span>
@@ -1594,28 +1594,40 @@ a:hover {
1594
1594
  align-items: center;
1595
1595
  }
1596
1596
 
1597
- .pin-input-row .chat-input {
1597
+ .pin-input-wrapper {
1598
+ position: relative;
1598
1599
  flex: 1;
1599
1600
  }
1600
1601
 
1601
- .pin-toggle {
1602
+ .pin-input-wrapper .chat-input {
1603
+ width: 100%;
1604
+ padding-left: 38px;
1605
+ }
1606
+
1607
+ .pin-toggle-left {
1608
+ position: absolute;
1609
+ left: 10px;
1610
+ top: 50%;
1611
+ transform: translateY(-50%);
1602
1612
  background: none;
1603
- border: 1px solid var(--border-strong);
1604
- border-radius: 50%;
1605
- width: 40px;
1606
- height: 40px;
1613
+ border: none;
1614
+ padding: 0;
1607
1615
  cursor: pointer;
1608
- font-size: 16px;
1609
1616
  color: var(--text-secondary);
1610
- flex-shrink: 0;
1611
1617
  display: flex;
1612
1618
  align-items: center;
1613
1619
  justify-content: center;
1614
- transition: border-color 0.15s;
1620
+ z-index: 1;
1621
+ transition: color 0.15s;
1615
1622
  }
1616
1623
 
1617
- .pin-toggle:hover {
1618
- border-color: var(--sage);
1624
+ .pin-toggle-left:hover {
1625
+ color: var(--sage);
1626
+ }
1627
+
1628
+ .pin-toggle-hidden {
1629
+ visibility: hidden;
1630
+ pointer-events: none;
1619
1631
  }
1620
1632
 
1621
1633
  .btn-primary {
@@ -395,8 +395,11 @@ async function* invokeAdminAgent(
395
395
  continue;
396
396
  }
397
397
 
398
- if ("result" in msg) {
398
+ if (msg.type === "result") {
399
399
  const usage = (msg as { usage?: { input_tokens?: number; output_tokens?: number } }).usage;
400
+ const log = agentLogStream("claude-agent-result");
401
+ log.write(`result msg: ${JSON.stringify({ usage, keys: Object.keys(msg) })}\n`);
402
+ log.end();
400
403
  if (usage?.input_tokens !== undefined) {
401
404
  yield { type: "usage", input_tokens: usage.input_tokens, output_tokens: usage.output_tokens ?? 0 };
402
405
  }
@@ -332,18 +332,20 @@ export default function AdminPage() {
332
332
  <div className="admin-pin-form">
333
333
  <form onSubmit={handleSetPin}>
334
334
  <div className="pin-input-row">
335
- <input
336
- ref={pinInputRef}
337
- type={showPin ? 'text' : 'password'}
338
- value={pin}
339
- onChange={e => setPin(e.target.value)}
340
- placeholder="Choose a PIN"
341
- className="chat-input"
342
- autoFocus
343
- />
344
- <button type="button" className="pin-toggle" onClick={() => setShowPin(!showPin)} aria-label={showPin ? 'Hide' : 'Show'}>
345
- {showPin ? <EyeOff size={18} /> : <Eye size={18} />}
346
- </button>
335
+ <div className="pin-input-wrapper">
336
+ <button type="button" className={`pin-toggle-left${!pin ? ' pin-toggle-hidden' : ''}`} onClick={() => setShowPin(!showPin)} aria-label={showPin ? 'Hide' : 'Show'}>
337
+ {showPin ? <EyeOff size={16} /> : <Eye size={16} />}
338
+ </button>
339
+ <input
340
+ ref={pinInputRef}
341
+ type={showPin ? 'text' : 'password'}
342
+ value={pin}
343
+ onChange={e => setPin(e.target.value)}
344
+ placeholder="Choose a PIN"
345
+ className="chat-input"
346
+ autoFocus
347
+ />
348
+ </div>
347
349
  </div>
348
350
  <div className="pin-input-row">
349
351
  <input
@@ -476,18 +478,20 @@ export default function AdminPage() {
476
478
  <div className="admin-pin-form">
477
479
  <form onSubmit={handleLogin}>
478
480
  <div className="pin-input-row">
479
- <input
480
- ref={pinInputRef}
481
- type={showPin ? 'text' : 'password'}
482
- value={pin}
483
- onChange={e => setPin(e.target.value)}
484
- placeholder="Enter PIN"
485
- className="chat-input"
486
- autoFocus
487
- />
488
- <button type="button" className="pin-toggle" onClick={() => setShowPin(!showPin)} aria-label={showPin ? 'Hide' : 'Show'}>
489
- {showPin ? <EyeOff size={18} /> : <Eye size={18} />}
490
- </button>
481
+ <div className="pin-input-wrapper">
482
+ <button type="button" className={`pin-toggle-left${!pin ? ' pin-toggle-hidden' : ''}`} onClick={() => setShowPin(!showPin)} aria-label={showPin ? 'Hide' : 'Show'}>
483
+ {showPin ? <EyeOff size={16} /> : <Eye size={16} />}
484
+ </button>
485
+ <input
486
+ ref={pinInputRef}
487
+ type={showPin ? 'text' : 'password'}
488
+ value={pin}
489
+ onChange={e => setPin(e.target.value)}
490
+ placeholder="Enter PIN"
491
+ className="chat-input"
492
+ autoFocus
493
+ />
494
+ </div>
491
495
  <button type="submit" className="chat-send" disabled={!pin}>
492
496
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
493
497
  <line x1="5" y1="12" x2="19" y2="12" />
@@ -4,7 +4,7 @@ You are the head of operations. Not an assistant waiting for instructions — a
4
4
 
5
5
  At the start of every session, check the graph for business context. Your goal is to populate the graph efficiently and comprehensively to maximise value from business intelligence.
6
6
 
7
- Your personalisation is in SOUL.md. Read it and apply it.
7
+ Your personalisation is in `agents/admin/SOUL.md` (relative to your working directory). Read it and apply it.
8
8
 
9
9
  ## Boundaries
10
10
 
@@ -18,7 +18,7 @@ Your personalisation is in SOUL.md. Read it and apply it.
18
18
  - Be proactive. Identify what needs doing and do it. Don't wait to be asked.
19
19
  - On session start, assess the state of the business from the graph and report what needs attention.
20
20
  - On first setup (incomplete business data in graph), immediately begin onboarding: learn the business, understand the stage, gather customer details, build a comprehensive picture so you can drive operations forward - one step at a time.
21
- - Write personalisation to your own SOUL.md. Also write the public agent's personalisation to agents/public/SOUL.md.
21
+ - Write personalisation to `agents/admin/SOUL.md`. Also write the public agent's personalisation to `agents/public/SOUL.md`.
22
22
  - Think strategically. Help with planning, not just tasks.
23
23
  - Surface problems before they become urgent. Recommend actions based on what you know.
24
24
  - Store everything you learn about the business in the graph — not in files.