create-pmos 0.60.2 → 0.60.3

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": "create-pmos",
3
- "version": "0.60.2",
3
+ "version": "0.60.3",
4
4
  "description": "Scaffold the PMOS portable kit — run the PMOS product-management workflow (initiative → lane-fit spec → contract → build → three gates, with work-type lanes for features, research, maintenance, incidents, and content) entirely inside your own repo, with all state as git-versioned files. Includes `workflow-sweep`, which audits the state every board derives from and separates real defects from work you simply have not done. `create-pmos workspace` also installs + launches the local Terminal Workspace GUI. No database, no network, no telemetry.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -1,3 +1,3 @@
1
- kernel-source: pmos@d57effd
1
+ kernel-source: pmos@dad3c31
2
2
  assembled: 2026-10-06
3
3
  mode: vendor
@@ -2,7 +2,7 @@
2
2
  type: Reference
3
3
  title: "Eval calibration log"
4
4
  description: "Append-only record of every PM override of an evaluator verdict."
5
- generated: {by: process:create-pmos, at: "2026-10-06T15:52:39Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-06T16:06:01Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  type: Reference
3
3
  title: "Health budgets"
4
4
  description: "Closed registry of health-budget anchors (D59), edited only by the PM."
5
- generated: {by: process:create-pmos, at: "2026-10-06T15:52:39Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-06T16:06:01Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  type: Reference
3
3
  title: "Discoveries and friction log"
4
4
  description: "Append-only record of what runs discovered and where the process rubbed."
5
- generated: {by: process:create-pmos, at: "2026-10-06T15:52:39Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-06T16:06:01Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -97,6 +97,8 @@ The bar at the top has Back (when you are deeper than a tab), Repository, the na
97
97
 
98
98
  Every row has a visible ⋯ button. Every control is at least 44 pixels high. Nothing works only when you hover.
99
99
 
100
+ You can have the same session open on your phone and your computer. A session has one size, so the screen you type in or tap last sets it. The other screen keeps showing the session and takes the size back as soon as you type in it.
101
+
100
102
  ## The older layout
101
103
 
102
104
  The address options `?layout=legacy` and `?fixture=cockpit` keep the older layouts. The rest of this page describes them. In the cockpit, the workspace has four columns: the repository rail, the list, the detail and the tabs. With `?layout=legacy` it has three columns: navigation, detail and tabs.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.60.2",
3
+ "version": "0.60.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.60.2",
3
+ "version": "0.60.3",
4
4
  "private": true,
5
5
  "description": "PMOS Terminal Workspace — local companion hosting real PTYs beside the library and gates (D62)",
6
6
  "license": "MIT",
@@ -378,10 +378,25 @@ function handle(m) {
378
378
  * The grid is therefore PUSHED here, unconditionally, the moment there is a session to push it
379
379
  * to. `sent*` is cleared first so a re-attach after a reconnect re-states it. */
380
380
  rec.sentCols = null; rec.sentRows = null;
381
- syncSize(rec);
381
+ syncSize(rec, true);
382
382
  refitVisible();
383
383
  return;
384
384
  }
385
+ if (m.t === 'size') {
386
+ /* ANOTHER SCREEN RESIZED THIS SESSION. A session has one PTY and one grid, and the last screen
387
+ * to state a grid wins. Without this, a pane never learned it had lost: its `sent*` still said
388
+ * its own grid, so it never stated it again, and the agent went on drawing for the other
389
+ * screen's grid inside this one — Claude Code's whole frame drawn twice, one under the other.
390
+ * The pane records the grid the PTY really has and YIELDS: it does not take the size back on
391
+ * its own (two open screens would then trade the size on every render), only when the person
392
+ * types into it, focuses it, or its own grid changes. See syncSize. */
393
+ for (const rec of panes.values()) {
394
+ if (rec.kind !== 'real' || rec.sessionId !== m.id || rec.attachedId !== m.id) continue;
395
+ rec.sentCols = m.cols; rec.sentRows = m.rows;
396
+ rec.yielded = !!rec.term && (rec.term.cols !== m.cols || rec.term.rows !== m.rows);
397
+ }
398
+ return;
399
+ }
385
400
  if (m.t === 'data') {
386
401
  const rec = recFor(m.id);
387
402
  if (rec) { try { rec.term.write(m.data); } catch { /* ignore */ } }
@@ -673,9 +688,11 @@ function attach(rec) {
673
688
  * rather than on what xterm reports, because the server holds the authoritative copy and a resize
674
689
  * this connection never sent is a resize the shell never heard.
675
690
  */
676
- function syncSize(rec) {
691
+ function syncSize(rec, claim) {
677
692
  if (!rec || rec.kind !== 'real' || !rec.term) return;
678
693
  if (!rec.sessionId || rec.attachedId !== rec.sessionId) return;
694
+ // A pane that another screen resized waits until the person uses it (`claim`). See `size`.
695
+ if (claim) rec.yielded = false; else if (rec.yielded) return;
679
696
  const cols = rec.term.cols, rows = rec.term.rows;
680
697
  if (!cols || !rows) return;
681
698
  if (rec.sentCols === cols && rec.sentRows === rows) return;
@@ -876,8 +893,11 @@ function buildReal(tab) {
876
893
  const rec = {
877
894
  kind: 'real', tab, host, term, fit, sessionId: null, attachedId: null, noted: null, ro: null,
878
895
  };
879
- term.onData((d) => { if (rec.sessionId) send({ t: 'input', id: rec.sessionId, data: d }); });
880
- term.onResize(() => syncSize(rec));
896
+ // Typing, focusing or re-sizing THIS pane takes the session's grid back from another screen, and
897
+ // the grid goes out before the keystroke so the agent answers it at this pane's size.
898
+ term.onData((d) => { if (rec.sessionId) { syncSize(rec, true); send({ t: 'input', id: rec.sessionId, data: d }); } });
899
+ term.onResize(() => syncSize(rec, true));
900
+ host.addEventListener('focusin', () => syncSize(rec, true));
881
901
  if (typeof ResizeObserver === 'function') {
882
902
  rec.ro = new ResizeObserver(() => refitVisible());
883
903
  try { rec.ro.observe(host); } catch { /* ignore */ }
@@ -8393,7 +8393,15 @@ wss.on('connection', (ws) => {
8393
8393
  } else if (m.t === 'input') {
8394
8394
  const s = sessions.get(m.id); if (s && s.pty) s.pty.write(m.data);
8395
8395
  } else if (m.t === 'resize') {
8396
- const s = sessions.get(m.id); if (s && s.pty) { try { s.pty.resize(m.cols, m.rows); s.cols = m.cols; s.rows = m.rows; } catch {} }
8396
+ const s = sessions.get(m.id);
8397
+ if (s && s.pty) {
8398
+ try { s.pty.resize(m.cols, m.rows); s.cols = m.cols; s.rows = m.rows; } catch {}
8399
+ // A session has ONE grid. Every other screen showing it is told the grid it now has, so a
8400
+ // pane drawn for a different size knows it lost the size and takes it back when used —
8401
+ // a phone and a desktop on one session otherwise each kept drawing the other's frames.
8402
+ const frame = JSON.stringify({ t: 'size', id: s.id, cols: s.cols, rows: s.rows });
8403
+ for (const c of s.clients || []) if (c !== ws && c.readyState === 1) c.send(frame);
8404
+ }
8397
8405
  } else if (m.t === 'new') {
8398
8406
  // `initiative` is what the client has always held and never sent: `+ session` is clicked on
8399
8407
  // an initiative row, the tab carries its `slug`, and the spawn message named three other