create-pmos 0.517.0 → 0.519.0

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.
@@ -25,15 +25,15 @@
25
25
  * Adler's moments (batch 17 item 4-B, AdM4): one face per section, where Claude speaks. Updates: glasses while it checks or installs,
26
26
  * holding the card when a version is ready, calm when up to date. General: holding the update card, else the Jira promise (only after
27
27
  * Commit). A failure in the status line takes the honest face, and then the section's face steps back (one Adler per screen).
28
- * The card at the bottom of General says when an update is ready. While a source is stale, an amber box at the top of every section names
29
- * it with ↻ Read again, and the place menu opens Settings at Connectors. Every write is the PM's press (pmPost carries the launch nonce).
28
+ * The card at the bottom of General says when an update is ready. While a source needs you (lib/source-heal.js), an amber box at the top of every section names
29
+ * it with its one action and what Claude tried, and the place menu opens Settings at Connectors. Every write is the PM's press (pmPost carries the launch nonce).
30
30
  * PMOS reads the sources and never posts to them.
31
31
  * openSettings(section?) */
32
32
  import { mountClaudeSetup } from './cozy-claude-setup.js';
33
33
  import * as JL from './cozy-jl.js';
34
34
  import { pmGet, pmPost, pressFetch } from './http.js';
35
35
  import { esc, svg, ICON, toast, wsParam, wsQ } from './cozy-evui.js';
36
- import { sourceRows, staleOf, partsOf } from './cozy-evmodel.js';
36
+ import { sourceRows, bannerOf } from './cozy-evmodel.js';
37
37
  import * as AD from './cozy-adler.js';
38
38
  import * as SE from './cozy-seasons.js';
39
39
  import { weeksOf, lineOf, storiesOf } from './upd-weeks.js';
@@ -182,11 +182,16 @@ function readsHtml() {
182
182
  const L = T.lines ? [['Today', T.lines.readLine(s, ['calendar'])], ['Needs you', T.lines.readLine(s, ['initiatives', 'my-work', 'd:brief'])]].filter((x) => x[1]) : [];
183
183
  return `${health}<div class="ev-reads">${rows}</div>${L.map(([k, v]) => `<p class="ev-small ev-mut ev-rl" data-reads="${esc(k)}"><b>${esc(k)}:</b> ${esc(v)}</p>`).join('')}`;
184
184
  }
185
- /** The amber box at the top of Settings while a source is stale (FmA3Settings): what is old, what it makes old, and ↻ Read again. */
185
+ /** The amber box at the top of Settings, only when the PM must act on a source (self-heal, feedback batch 26 item 7). While Claude
186
+ * reads a source again by itself, or after it did, there is no box. The box names the one action and links to the exact place:
187
+ * the Claude connectors, Settings › Claude, or ↻ when only a press reads it. It says what Claude already tried. */
186
188
  function staleHtml() {
187
- const st = T.fresh && T.fresh.ok ? staleOf(T.fresh.sources) : null;
188
- if (!st) return '';
189
- return `<div class="ev-warn ev-line ev-st-warn" id="srcWarn"><i class="ev-dot risk" aria-hidden="true"></i><p class="ev-grow"><b>${esc(st.state === 'stale' ? st.name + ' was last read ' + st.ago + '.' : st.text + '.')}</b> ${esc(partsOf(st.id))} may be out of date.</p>${readBtn(st.id, 'Read ' + st.name + ' again', 'ev-ib ev-ib-card')}</div>`;
189
+ const b = T.fresh && T.fresh.ok ? bannerOf(T.fresh.sources) : null;
190
+ if (!b) return '';
191
+ const a = b.action;
192
+ const act = a.read ? readBtn(b.id, a.label, 'ev-ib ev-ib-card')
193
+ : `<a class="ev-ib ev-ib-card" href="${esc(a.href || '#' + a.sec)}"${a.href ? ' target="_blank" rel="noopener"' : ` data-ev-sec="${esc(a.sec)}"`} aria-label="${esc(a.label)}" title="${esc(a.label)}">${svg(ICON.open, 18)}</a>`;
194
+ return `<div class="ev-warn ev-line ev-st-warn" id="srcWarn" data-cause="${esc(b.cause || '')}"><i class="ev-dot risk" aria-hidden="true"></i><p class="ev-grow"><b>${esc(b.title)}</b> ${esc(b.body)}${a.read ? '' : ' ' + esc(a.how)}</p>${act}</div>`;
190
195
  }
191
196
  function connectors() {
192
197
  if (T.src === null) return readsHtml() + '<p class="ev-small ev-mut">Reading the connectors.</p>';
@@ -265,18 +270,19 @@ function claudeSec() {
265
270
  return '<h4>Claude login and profile</h4><div data-claude-setup></div>'
266
271
  + trow('API key', keySub, keyRight)
267
272
  + (k.present && C.off ? trow('Runs on the API', 'Off: PMOS_GRILL_API is off in the environment.', '<span class="ev-pill">Off</span>') : '')
268
- + sharpenRows(k.present);
273
+ + sharpenRows();
269
274
  }
270
275
  /* Settings › Claude › Sharpen prompts (batch 26 item 2; pm/next-prompt-ui.js, lib/routes-next-prompt.js): the suggestions after a reply, Sharpen as I
271
- * type ('auto' is on when Claude runs on the API key, where a sharpen takes about 2 s), the style, and the PM's own rules for Your own.
276
+ * type (on by default: both run as a background `claude -p` in the PM's own Claude session, Wawan 2026-11-04), the style, and Your own rules.
272
277
  * The same style and switch are in the chat's Sharper card menu; a save here tells the chat (the event pmos:prompt-prefs). */
273
278
  const NPK = { 'prompt.next': ['nextSet', 'next'], 'prompt.sharpen': ['sharpenSet', 'sharpen'], 'prompt.style': ['style', 'style'], 'prompt.rules': ['rulesText', 'rules'] }; // row key → [GET field, POST key]
274
279
  const setVal = (k, d) => { const v = T.np ? T.np[NPK[k][0]] : undefined; return v !== undefined && v !== null ? v : d; };
275
- function sharpenRows(api) {
280
+ function sharpenRows() {
276
281
  const next = setVal('prompt.next', true) !== false, sh = setVal('prompt.sharpen', 'auto'), style = setVal('prompt.style', 'engineer'), rules = String(setVal('prompt.rules', '') || '');
277
- const on = sh === 'on' || (sh === 'auto' && api);
278
- const shSub = sh === 'auto' ? (api ? 'On, because Claude runs on your API key: the sharper version shows in about 2 seconds.' : 'Off for now: without an API key each one takes 10 seconds or more. Turn it on to use it anyway.')
279
- : on ? 'When you pause on a rough prompt, a sharper version shows above the box.' : 'Off. No spinner and no Sharper card.';
282
+ const on = sh !== 'off' && !(T.np && T.np.sharpen === false && sh === 'auto');
283
+ const down = T.np && T.np.apiDown;
284
+ const shSub = !on ? (down && down.auth ? 'Off for now: your API key was refused. Replace it under API key above.' : 'Off. No spinner and no Sharper card.')
285
+ : 'When you pause on a rough prompt, Claude writes a sharper version above the box. It runs in your Claude session and takes a few seconds.';
280
286
  const styleSub = style === 'polish' ? 'Clearer and shorter, your meaning kept.' : style === 'own' ? (rules.trim() ? 'Your rules below.' : 'Write your rules below first.') : 'Builds the best prompt for the job.';
281
287
  return '<h4>Sharpen prompts</h4>'
282
288
  + trow('Suggest the next prompt', 'After a reply, up to two prompts above the box. A tap puts one in the box. Nothing sends.', sw(next, 'data-ev-np="prompt.next"', 'Suggest the next prompt'))
@@ -315,8 +321,8 @@ function draw() {
315
321
  // a redraw replaces the nodes: keep focus on the same control (by its data-ev-* key), else on the open section, never on the body
316
322
  const ae = document.activeElement, had = ae && el.contains(ae), key = had ? [...ae.attributes].find((a) => a.name.startsWith('data-ev-')) : null;
317
323
  const n = { connectors: T.src === null ? '' : Object.keys(SRC).filter((k) => T.src[k]).length + T.conn.filter((c) => !c.source && T.con[c.name]).length, workspaces: T.ws ? T.ws.filter((x) => !x.linked).length : '', updates: ready() ? '<span class="ev-pill y">1</span>' : '' };
318
- const stale = T.fresh && T.fresh.ok ? staleOf(T.fresh.sources) : null;
319
- el.querySelector('.ev-st-nav').innerHTML = '<h2 class="ev-h2">Settings</h2>' + SECTIONS.map(([k, l]) => PAGE_LINKS[k] ? `<a href="${PAGE_LINKS[k]}" class="ev-mi">${esc(l)}</a>` : `<a href="#${k}" class="ev-mi" data-ev-sec="${k}"${T.sec === k ? ' aria-current="page"' : ''}>${esc(l)}${k === 'connectors' && stale ? '<i class="ev-dot risk" aria-label="A source is stale" role="img"></i>' : ''}${n[k] !== undefined && n[k] !== '' ? `<span class="ev-k">${typeof n[k] === 'number' ? n[k] : n[k]}</span>` : ''}</a>`).join('');
324
+ const stale = T.fresh && T.fresh.ok ? bannerOf(T.fresh.sources) : null;
325
+ el.querySelector('.ev-st-nav').innerHTML = '<h2 class="ev-h2">Settings</h2>' + SECTIONS.map(([k, l]) => PAGE_LINKS[k] ? `<a href="${PAGE_LINKS[k]}" class="ev-mi">${esc(l)}</a>` : `<a href="#${k}" class="ev-mi" data-ev-sec="${k}"${T.sec === k ? ' aria-current="page"' : ''}>${esc(l)}${k === 'connectors' && stale ? '<i class="ev-dot risk" aria-label="A source needs you" role="img"></i>' : ''}${n[k] !== undefined && n[k] !== '' ? `<span class="ev-k">${typeof n[k] === 'number' ? n[k] : n[k]}</span>` : ''}</a>`).join('');
320
326
  const label = (SECTIONS.find((x) => x[0] === T.sec) || SECTIONS[0])[1];
321
327
  const all = T.sec === 'connectors' ? `<button type="button" class="ev-ib" data-ev-readall aria-label="Read every source again" title="Read all again"${T.reading ? ' disabled' : ''}>${svg(RD, 18)}</button>` : '';
322
328
  el.querySelector('.ev-st-main').innerHTML = staleHtml() + `<div class="ev-top"><h3 class="ev-h2" id="evStTitle">${esc(label)}</h3>${all}<button type="button" class="ev-ib" data-ev-close aria-label="Close settings" title="Close settings">${svg(ICON.close, 18)}</button></div>`
@@ -375,7 +381,7 @@ async function press(e) {
375
381
  }
376
382
  if (t.dataset.evSeg === 'prompt.style') { await savePrompt('prompt.style', t.dataset.v, 'Saved. Sharper prompts follow this style.'); return; }
377
383
  if (t.dataset.evNp === 'prompt.next') { const on = setVal('prompt.next', true) === false; await savePrompt('prompt.next', on, on ? 'Saved. Claude suggests the next prompt after a reply.' : 'Saved. No suggestions after a reply.'); return; }
378
- if (t.dataset.evNp === 'prompt.sharpen') { const api = !!(T.cl && T.cl.key && T.cl.key.present), sh = setVal('prompt.sharpen', 'auto'), was = sh === 'on' || (sh === 'auto' && api); await savePrompt('prompt.sharpen', was ? 'off' : 'on', was ? 'Saved. Sharpen as I type is off.' : 'Saved. Sharpen as I type is on.'); return; }
384
+ if (t.dataset.evNp === 'prompt.sharpen') { const sh = setVal('prompt.sharpen', 'auto'), was = sh !== 'off' && !(T.np && T.np.sharpen === false && sh === 'auto'); await savePrompt('prompt.sharpen', was ? 'off' : 'on', was ? 'Saved. Sharpen as I type is off.' : 'Saved. Sharpen as I type is on.'); return; }
379
385
  if ('evRulesave' in t.dataset) { const ta = T.el.querySelector('#evRules'); await savePrompt('prompt.rules', ta ? ta.value.slice(0, 1000) : '', 'Saved. Your own rules are used when Sharpen as is Your own.'); return; }
380
386
  if (t.dataset.evSeg && t.dataset.evSeg.startsWith('adler.')) { const r = await AD.savePref(pmPost, t.dataset.evSeg, t.dataset.v); T.msg = r && r.ok ? 'Saved.' : (r && r.error) || 'Not saved.'; draw(); return; }
381
387
  if (t.dataset.evDress) {
@@ -512,6 +518,7 @@ async function load() {
512
518
  export function openSettings(section) {
513
519
  const el = mount();
514
520
  if (section === 'dress') { T.sec = 'adler'; T.dress = true; } else if (section && BODY[section]) { T.sec = section; T.dress = false; }
521
+ else if (el.hidden) { T.sec = 'general'; T.dress = false; } // a fresh open with no section opens on General (R2), not on the section of the last visit
515
522
  if (el.hidden) { const ae = document.activeElement; T.back = ae && ae !== document.body ? ae : null; T.ws = null; T.src = null; T.msg = ''; T.phase = ''; T.auto = false; T.wkOpen = new Set(); T.wkMore = new Set(); T.arc = false; T.fresh = null; T.note = {}; T.dset = {}; T.cl = null; T.gr = null; T.np = null; JL.reset(); }
516
523
  el.hidden = false; document.getElementById('evStScrim').hidden = false; el.setAttribute('data-esc-layer', '');
517
524
  draw();
@@ -1,10 +1,15 @@
1
+ @font-face{font-family:"Space Grotesk";src:url(/vendor/space-grotesk-var.woff2) format("woff2");font-weight:300 700;font-display:swap}
2
+ @font-face{font-family:"IBM Plex Mono";src:url(/vendor/ibm-plex-mono-400.woff2) format("woff2");font-weight:400;font-display:swap}
3
+ @font-face{font-family:"IBM Plex Mono";src:url(/vendor/ibm-plex-mono-500.woff2) format("woff2");font-weight:500;font-display:swap}
4
+ @font-face{font-family:"IBM Plex Mono";src:url(/vendor/ibm-plex-mono-700.woff2) format("woff2");font-weight:600 700;font-display:swap}
5
+ :root{--fw-regular:400;--fw-strong:600;--fs-field:16px;--fs-display:26px;--fs-page:26px;--fs-panel:18px;--fs-section:15px;--fs-row:15px;--fs-body:15px;--fs-meta:13px;--fs-label:13px;--fs-badge:13px;--lh-display:1.2;--lh-page:1.2;--lh-panel:1.3;--lh-section:1.35;--lh-row:1.4;--lh-body:1.6;--lh-meta:1.5;--lh-label:1.35;--lh-badge:1.2}
1
6
  /* premium-vw-guide.css — the guide in the Viewer (premium-vw-guide.js; canvas Exploring › User guide, option B): a page rail on the left,
2
7
  * the page on the right with its section chips. Under 720 px the rail hides and the list icon shows it over the page. Quiet: hairlines, one
3
8
  * well for the open page, no colour but the chips' hover. */
4
- .vwdoc.gd{padding:0}
9
+ .vwdoc.gd{padding:0;max-width:none}
5
10
  .gd-wrap{container-type:inline-size;display:flex;align-items:flex-start;gap:20px;position:relative}
6
11
  .gd-rail{width:248px;flex:none;position:sticky;top:0;max-height:calc(100vh - 140px);overflow:auto;padding:4px 4px 16px 0;display:flex;flex-direction:column;gap:2px}
7
- .gd-read{flex:1;min-width:0}
12
+ .gd-read{flex:1;min-width:0;max-width:none}
8
13
  .gd-tog{display:none}
9
14
  .gd-find{display:flex;align-items:center;gap:8px;height:40px;padding:0 10px;margin:0 0 6px;border-radius:10px;background:var(--well);color:var(--ink-3)}
10
15
  .gd-find .i{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;flex:none}
@@ -4475,7 +4475,7 @@ function startHeadless(base, workdir, kind, launchArgs, env, initialInput, addDi
4475
4475
  // grandchild keeps the output pipes open after the run is stopped.
4476
4476
  // Windows: `claude` installed by npm is claude.cmd, which spawn() cannot start by name (lib/win-command.js).
4477
4477
  const run = winCommand.resolve(base.agent, { env });
4478
- try { child = spawn(run.file, [...run.prefix, ...args], { cwd: workdir, env, stdio: ['pipe', 'pipe', 'pipe'], detached: true }); }
4478
+ try { child = spawn(run.file, [...run.prefix, ...args], { cwd: workdir, env: headlessLib.headlessEnv(env), stdio: ['pipe', 'pipe', 'pipe'], detached: true }); }
4479
4479
  catch (e) {
4480
4480
  s.state = 'exited'; s.note = spawnFailureAdvice(e, base.agent, false);
4481
4481
  say(`\x1b[38;5;203m✗ ${s.note}\x1b[0m\n`); persist(s); pushSessions(); pushWorkspaces(); return s;