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 +1 -1
- package/payload/KIT-VERSION +1 -1
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/state/discoveries.md +1 -1
- package/workspace/c3-sessions-panel-test.js +12 -3
- package/workspace/guide/console3-sessions.md +2 -0
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/c3-session-term.js +15 -1
- package/workspace/public/pm/c3-shell.js +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "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": {
|
package/payload/KIT-VERSION
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
9
|
+
"version": "0.178.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/workspace/package.json
CHANGED
|
@@ -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)
|
|
112
|
-
|
|
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());
|