create-pmos 0.177.0 → 0.178.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pmos",
3
- "version": "0.177.0",
3
+ "version": "0.178.0",
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@9ea3e51
1
+ kernel-source: pmos@066089d
2
2
  assembled: 2026-10-13
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-13T01:28:03Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-13T01:59:58Z"}
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-13T01:28:03Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-13T01:59:58Z"}
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-13T01:28:03Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-13T01:59:58Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -30,6 +30,12 @@ const finish = async (code) => {
30
30
  process.on('unhandledRejection', (e) => { console.error(e); finish(1); });
31
31
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
32
32
  async function until(fn, ms = 8000) { const end = Date.now() + ms; while (Date.now() < end) { const v = await fn(); if (v) return v; await sleep(150); } return null; }
33
+ /* the Sessions panel: the Sessions button opens a running last session's terminal, whose "All sessions" opens the panel */
34
+ async function openPanel(page) {
35
+ await page.click('[data-c3pg="agent-button"]');
36
+ const got = await page.waitForSelector('[data-c3t="host"], [data-c3-all-sessions]', { timeout: 15000 }).catch(() => null);
37
+ if (got && await page.$('[data-c3-all-sessions]')) await page.click('[data-c3-all-sessions]');
38
+ }
33
39
  async function waitFor(page, fn, arg, ms = 15000) { try { await page.waitForFunction(fn, arg, { timeout: ms }); return true; } catch { return false; } }
34
40
  const get = (p) => fetch(BASE + p).then((r) => r.json());
35
41
  // the agent CLI's home for this run: the conversation test writes a session record and a transcript here, as claude would
@@ -175,8 +181,11 @@ function startShell(name) {
175
181
  await waitFor(page, () => !document.querySelector('[data-c3-session-term]'));
176
182
 
177
183
  /* ── D: End asks first, then ends ── */
184
+ // Sessions opens the terminal of the session opened last while it runs (PM, 2026-10-13); its "All sessions" opens the panel
178
185
  await page.click('[data-c3pg="agent-button"]');
179
- ok('D0 the panel opens again', await waitFor(page, (s) => !!document.querySelector(s), sel));
186
+ ok('D0 Sessions opens the terminal of the session opened last', await waitFor(page, (i) => !!document.querySelector(`[data-c3-session-term="${i}"] .xterm`), id));
187
+ await page.click('[data-c3-all-sessions]');
188
+ ok('D0a All sessions in the terminal sheet opens the panel', await waitFor(page, (s) => !!document.querySelector(s), sel) && !(await page.$('[data-c3-session-term]')));
180
189
  ok('D0b it opens on the session opened last: its row is open and marked', await waitFor(page, (i) => { const li = document.querySelector(`[data-c3t="mine"] [data-run="${i}"]`); const end = document.querySelector(`[data-end-session="${i}"]`); return !!li && li.hasAttribute('data-last-open') && !!end && end.offsetParent !== null; }, id)
181
190
  && await page.evaluate((i) => localStorage.getItem('pmos.workspace.c3.last-session') === i, id));
182
191
  if (!(await page.isVisible(`[data-end-session="${id}"]`).catch(() => false))) await page.click(sel + ' .nb');
@@ -232,7 +241,7 @@ function startShell(name) {
232
241
 
233
242
  /* ── F: New session starts on console3 and opens its terminal here (c3-new-session; it went to the classic app before) ── */
234
243
  const before = new Set(((await get('/api/sessions')).sessions || []).map((s) => s.id));
235
- if (!(await page.isVisible('[data-c3t="new"]').catch(() => false))) await page.click('[data-c3pg="agent-button"]');
244
+ if (!(await page.isVisible('[data-c3t="new"]').catch(() => false))) await openPanel(page);
236
245
  ok('F1 Started by me has a New button', await waitFor(page, () => !!document.querySelector('[data-c3t="new"]')));
237
246
  await page.click('[data-c3t="new"]');
238
247
  ok('F2 New opens the new-session sheet on this page', await waitFor(page, () => !!document.querySelector('[data-c3ns="form"]')) && page.url() === url0, page.url());
@@ -261,7 +270,7 @@ function startShell(name) {
261
270
 
262
271
  /* ── P: a past session can be removed, also one the server knows only from its record on disk ── */
263
272
  if (await page.isVisible('[data-shell="sheet"] [aria-label="Close"]').catch(() => false)) await page.click('[data-shell="sheet"] [aria-label="Close"]');
264
- if (!(await page.isVisible('[data-c3t="past"]').catch(() => false))) await page.click('[data-c3pg="agent-button"]');
273
+ if (!(await page.isVisible('[data-c3t="past"]').catch(() => false))) await openPanel(page);
265
274
  const pastSel = '[data-c3t="past"] [data-run="sess-9901"]';
266
275
  ok('P1 the ended session is listed in Past', await waitFor(page, (s) => !!document.querySelector(s), pastSel));
267
276
  if (!(await page.isVisible('[data-end-session="sess-9901"]').catch(() => false))) await page.click(pastSel + ' .nb');
@@ -9,6 +9,8 @@ timestamp: 2026-10-13
9
9
 
10
10
  Press the **Sessions** button in the console3 header to open the panel. It is the bolt icon with no label. Point at it to see its name. When a session needs you, a count shows on it. It shows your sessions in four groups: **On a card**, **Started by me**, **Past (7 days)** and **Automatic**.
11
11
 
12
+ When the session that you opened last is still running, the **Sessions** button opens its terminal directly. To see all your sessions, press **All sessions** at the top of the terminal sheet. If that session has ended, the button opens the panel.
13
+
12
14
  The panel opens on the session that you opened last. Its row is open, so you can read the conversation and send a message without searching for it. A session counts as opened when you open its row or its terminal.
13
15
 
14
16
  ## The dock
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.177.0",
3
+ "version": "0.178.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pmos-workspace",
9
- "version": "0.177.0",
9
+ "version": "0.178.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@xterm/addon-fit": "^0.11.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.177.0",
3
+ "version": "0.178.0",
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",
@@ -23,6 +23,17 @@ export const PAD_H_KEY = 'pmos.workspace.c3.term-pad-h';
23
23
  export const LAST_SESSION_KEY = 'pmos.workspace.c3.last-session';
24
24
  export const rememberSession = (id) => { if (id) store.set(LAST_SESSION_KEY, String(id)); };
25
25
  export const lastSession = () => store.get(LAST_SESSION_KEY) || null;
26
+
27
+ /** The header's Sessions button: the terminal of the session opened last, when it still runs; else the Sessions panel (c3-terminal.js).
28
+ * The terminal sheet has "All sessions", which opens the panel. */
29
+ export async function openLastSession() {
30
+ const id = lastSession();
31
+ const r = id ? await pmGet('/api/sessions').catch(() => null) : null;
32
+ const s = r && Array.isArray(r.sessions) ? r.sessions.find((x) => x.id === id && x.state !== 'exited' && x.shown !== 'exited') : null;
33
+ if (s) return openSessionTerm({ id: s.id, name: s.name });
34
+ const m = await import('./c3-terminal.js');
35
+ return m.openTerminalPage();
36
+ }
26
37
  const PAD_ROWS = 4; // the default height
27
38
  /** The pad's height in rows: at least 2, and at most what leaves the terminal its room in a column `room` px high with `lh` px rows. Pure, for Node. */
28
39
  export const clampPad = (rows, room, lh) => Math.max(2, Math.min(Math.round(rows), Math.max(2, Math.floor((room - 200) / (lh || 18)))));
@@ -138,10 +149,13 @@ export async function openSessionTerm({ id, name } = {}) {
138
149
  let size = sizeOf(store.get(SIZE_KEY));
139
150
  const sizeBtns = SIZES.map(([k, label, title]) => c3btn({ 'aria-pressed': String(k === size), 'data-c3-size': k, title, 'aria-label': title }, () => setSize(k), label));
140
151
  const seg = el('div', { class: 'seg c3t-size', role: 'group', 'aria-label': 'Terminal size', 'data-c3-size-seg': '' }, ...sizeBtns);
152
+ // the way back to every session: the Sessions panel, in place of this sheet
153
+ const all = c3btn({ class: 'chip2 lk', 'data-c3-all-sessions': '', title: 'All sessions: the Sessions panel' }, () => { sheet.close(); import('./c3-terminal.js').then((m) => m.openTerminalPage()); },
154
+ c3ic('menu', 'sm'), 'All sessions');
141
155
  const cls = (k) => 'tsz-' + k;
142
156
  // Esc and Tab belong to the shell while the terminal has focus (Esc interrupts an agent); the close button and a press outside still close.
143
157
  // The scratchpad is outside the terminal host, so its keys are the page's own.
144
- const sheet = openSheet({ title: name || id, icon: 'term', wide: true, body: wrap, actions: seg, cls: cls(size),
158
+ const sheet = openSheet({ title: name || id, icon: 'term', wide: true, body: wrap, actions: [all, seg], cls: cls(size),
145
159
  onClose: () => { pad.dispose(); term.disposeTerm(tab); }, passKeys: (e) => host.contains(e.target) });
146
160
  function setSize(k) {
147
161
  sheet.sheet.classList.remove(cls(size)); size = sizeOf(k); sheet.sheet.classList.add(cls(size));
@@ -108,8 +108,9 @@ function header() {
108
108
  S.ttl = el('h1', { 'data-c3pg': 'title' });
109
109
  S.sub = el('div', { class: 'sub', 'data-c3pg': 'date' }, dateLine());
110
110
  S.termBdg = el('span', { class: 'bdg', hidden: true, 'data-c3pg': 'term-count' }, '0');
111
- /* the AGENT button (the bolt glyph, as the mockup's Sessions pill): the Terminal page, the full agent surfaces, with the needs-you badge. It is an icon only (PM, 2026-10-12: no "Sessions" label); its tooltip and aria-label name it. Ctrl J and the More menu fold and open the dock. */
112
- S.termBtn = button({ variant: 'ghost', label: [ic('bolt', 'sm'), S.termBdg], onClick: () => { import('./c3-terminal.js').then((m) => m.openTerminalPage()); },
111
+ /* the AGENT button (the bolt glyph, as the mockup's Sessions pill), with the needs-you badge: the terminal of the session opened last while it runs
112
+ * (PM, 2026-10-13), else the Terminal page, the full agent surfaces; the terminal sheet's "All sessions" opens that page. It is an icon only (PM, 2026-10-12: no "Sessions" label); its tooltip and aria-label name it. Ctrl J and the More menu fold and open the dock. */
113
+ S.termBtn = button({ variant: 'ghost', label: [ic('bolt', 'sm'), S.termBdg], onClick: () => { import('./c3-session-term.js').then((m) => m.openLastSession()); },
113
114
  attrs: { class: 'psess', title: 'Agent surfaces: sessions, results to accept, questions for you', 'aria-label': 'Agent surfaces: sessions, results to accept, questions for you', 'aria-haspopup': 'dialog', 'data-c3pg': 'agent-button' } }); // the mockup's header button (agent-surfaces.html #act)
114
115
  S.tools = el('div', { class: 'tools', 'data-c3pg': 'tools' });
115
116
  if (S.demoMod) S.tools.append(S.demoMod.demoPill());