create-pmos 0.520.0 → 0.521.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 +14 -16
- 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/changelog.json +13 -0
- package/workspace/guide/next-prompt.md +4 -4
- package/workspace/lib/routes-next-prompt.js +12 -4
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/cozy-home.css +2 -5
- package/workspace/public/pm/next-prompt-ui.js +6 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.521.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": {
|
|
@@ -32,6 +32,19 @@
|
|
|
32
32
|
"audit"
|
|
33
33
|
],
|
|
34
34
|
"pmosChanges": [
|
|
35
|
+
{
|
|
36
|
+
"v": "0.521.0",
|
|
37
|
+
"at": "2026-11-04T02:17:43.880Z",
|
|
38
|
+
"f": 4,
|
|
39
|
+
"prs": [
|
|
40
|
+
{
|
|
41
|
+
"n": 1217,
|
|
42
|
+
"t": "Sharper prompt keeps to the composer column and starts lean",
|
|
43
|
+
"s": "Sharper prompt keeps to the composer column and starts lean",
|
|
44
|
+
"k": "fix"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
35
48
|
{
|
|
36
49
|
"v": "0.520.0",
|
|
37
50
|
"at": "2026-11-04T01:52:53.932Z",
|
|
@@ -381,21 +394,6 @@
|
|
|
381
394
|
"d": "The Timeline still cut long task names short. Now they wrap in full there too."
|
|
382
395
|
}
|
|
383
396
|
]
|
|
384
|
-
},
|
|
385
|
-
{
|
|
386
|
-
"v": "0.501.0",
|
|
387
|
-
"at": "2026-11-03T04:49:22.234Z",
|
|
388
|
-
"f": 4,
|
|
389
|
-
"prs": [
|
|
390
|
-
{
|
|
391
|
-
"n": 1184,
|
|
392
|
-
"t": "Home's list toggle always opens and closes the list",
|
|
393
|
-
"s": "Home's list toggle always opens and closes the list",
|
|
394
|
-
"k": "fix",
|
|
395
|
-
"h": "Home's list opens and closes",
|
|
396
|
-
"d": "On a wide Viewer, Home's list could fold away and refuse to open again. Now the toggle opens and closes it every time."
|
|
397
|
-
}
|
|
398
|
-
]
|
|
399
397
|
}
|
|
400
398
|
]
|
|
401
399
|
}
|
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-11-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-11-04T02:17:40Z"}
|
|
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-11-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-11-04T02:17:40Z"}
|
|
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-11-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-11-04T02:17:40Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
package/workspace/changelog.json
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"v": "0.521.0",
|
|
4
|
+
"at": "2026-11-04T02:17:43.880Z",
|
|
5
|
+
"f": 4,
|
|
6
|
+
"prs": [
|
|
7
|
+
{
|
|
8
|
+
"n": 1217,
|
|
9
|
+
"t": "Sharper prompt keeps to the composer column and starts lean",
|
|
10
|
+
"s": "Sharper prompt keeps to the composer column and starts lean",
|
|
11
|
+
"k": "fix"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
},
|
|
2
15
|
{
|
|
3
16
|
"v": "0.520.0",
|
|
4
17
|
"at": "2026-11-04T01:52:53.932Z",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Guide
|
|
3
3
|
title: "The next prompt, suggested and sharpened"
|
|
4
4
|
description: "After a reply, the chat suggests what to say next. While you type a rough prompt, Claude offers a sharper version. What each card means, the styles, the settings, and the guardrails."
|
|
5
|
-
timestamp: 2026-11-
|
|
5
|
+
timestamp: 2026-11-04
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# The next prompt, suggested and sharpened
|
|
@@ -30,7 +30,7 @@ Each card names only the initiatives, tasks and PRs that the reply or your messa
|
|
|
30
30
|
|
|
31
31
|
## While you type
|
|
32
32
|
|
|
33
|
-
When you stop typing for about
|
|
33
|
+
When you stop typing for about 2 seconds on a rough prompt of three or more words, one quiet line, **Sharpening…**, shows above the box while Claude sharpens it. This takes a few seconds, and you can keep typing. Then one lit **Sharper** card shows. If you typed on, or no answer comes in 30 seconds, nothing shows. Tap it to put the sharper text in the box. ↶ in the box puts back what you wrote. When the sharper version says the same as yours, no card shows.
|
|
34
34
|
|
|
35
35
|
The sliders icon on the Sharper card sets how Claude sharpens:
|
|
36
36
|
|
|
@@ -38,14 +38,14 @@ The sliders icon on the Sharper card sets how Claude sharpens:
|
|
|
38
38
|
- **Prompt engineer** (the default): the best prompt for the job. It names the real initiative or task and says what a good answer holds. A date move or a cut becomes a rehearsal that waits for your yes.
|
|
39
39
|
- **Your own**: your own wording rules. You write them once in Settings.
|
|
40
40
|
|
|
41
|
-
The same menu has the **Sharpen as I type** switch. Off means no
|
|
41
|
+
The same menu has the **Sharpen as I type** switch. Off means no Sharpening… line and no Sharper card.
|
|
42
42
|
|
|
43
43
|
## Settings › Claude › Sharpen prompts
|
|
44
44
|
|
|
45
45
|
| Row | What it does |
|
|
46
46
|
|---|---|
|
|
47
47
|
| Suggest the next prompt | On by default. Off: no cards after a reply. |
|
|
48
|
-
| Sharpen as I type | On by default. Off: no
|
|
48
|
+
| Sharpen as I type | On by default. Off: no Sharpening… line and no Sharper card. |
|
|
49
49
|
| Sharpen as | Polish, Prompt engineer or Your own. |
|
|
50
50
|
| Your own rules | The rules for Your own, up to 1000 characters. |
|
|
51
51
|
|
|
@@ -31,6 +31,11 @@ const ROOT = path.join(__dirname, '..'); // the workspace package: the files t
|
|
|
31
31
|
const FIXTURE = path.join(ROOT, 'fixtures/next-prompt-replay.json');
|
|
32
32
|
const KEEP = 30;
|
|
33
33
|
const TIMEOUT_MS = 90000;
|
|
34
|
+
const SHARPEN_TIMEOUT_MS = 30000; // a sharper prompt that comes later than this is for words the PM has typed past: drop it
|
|
35
|
+
/* A background run for these two short jobs starts lean (hotfix 2026-11-04, Wawan: "Loading too long"): a fixed fast model at low effort,
|
|
36
|
+
* no MCP servers, no skills, only the PM's user settings (no project hooks such as a session-start script) and one read tool. Measured on
|
|
37
|
+
* one sharpen prompt: about 61,000 input tokens and 7 s before, about 10,000 and 5 s after; the rest is the CLI's own start. */
|
|
38
|
+
const LEAN_FLAGS = Object.freeze([{ flag: '--strict-mcp-config' }, { flag: '--disable-slash-commands' }, { flag: '--setting-sources', value: 'user' }, { flag: '--tools', value: 'Read' }]);
|
|
34
39
|
const DEFAULT_MODEL = 'claude-sonnet-5-5';
|
|
35
40
|
|
|
36
41
|
const text = (v, n) => (typeof v === 'string' ? v.slice(0, n) : '');
|
|
@@ -112,16 +117,18 @@ async function viaApi(key, prompt, env, create = null) {
|
|
|
112
117
|
return null;
|
|
113
118
|
}
|
|
114
119
|
/** A background read-only Claude run → its answer text, or null after `fail(why)` says why it gave none. */
|
|
115
|
-
function viaHeadless(ctx, prompt, origin, fail = () => {}) {
|
|
120
|
+
function viaHeadless(ctx, prompt, origin, fail = () => {}, { timeoutMs = TIMEOUT_MS } = {}) {
|
|
116
121
|
const headless = require('./headless.js');
|
|
117
122
|
if (typeof ctx.spawnSession !== 'function') { fail('no background run here'); return Promise.resolve(null); }
|
|
118
123
|
if (headless.kindFor({ agent: 'claude', origin }) !== 'ask') { fail('the run would not be read-only'); return Promise.resolve(null); }
|
|
119
124
|
return new Promise((resolve) => {
|
|
120
125
|
let settled = false; const done = (v, why) => { if (settled) return; settled = true; clearTimeout(t); if (v == null) fail(why); resolve(v); };
|
|
121
|
-
const t = setTimeout(() => done(null, 'the run took longer than ' + Math.round(
|
|
126
|
+
const t = setTimeout(() => done(null, 'the run took longer than ' + Math.round(timeoutMs / 1000) + ' s'), timeoutMs);
|
|
122
127
|
let s = null;
|
|
123
128
|
try {
|
|
129
|
+
const model = String(envOf(ctx).PMOS_NEXT_PROMPT_MODEL || '').trim() || DEFAULT_MODEL;
|
|
124
130
|
s = ctx.spawnSession({ name: 'chat: next prompt', agent: 'claude', origin, initialInput: prompt,
|
|
131
|
+
modelAsk: { explicit: { model, effort: 'low' } }, launchFlags: LEAN_FLAGS.map((f) => ({ ...f })),
|
|
125
132
|
onDone: (d) => { const r = d && d.result; done(r && r.ok && !r.isError ? String(r.text || '') : null, 'the run ended without an answer' + (d && d.note ? ': ' + String(d.note).slice(0, 80) : '')); } });
|
|
126
133
|
} catch (e) { s = null; }
|
|
127
134
|
if (!s || s.state === 'exited') done(null, 'the run did not start' + (s && s.note ? ': ' + String(s.note).slice(0, 80) : ''));
|
|
@@ -145,7 +152,8 @@ async function ask(ctx, prompt, { job, key, run }) {
|
|
|
145
152
|
catch (e) { failed(apiWhy(e), e && (e.status === 401 || e.status === 403)); }
|
|
146
153
|
}
|
|
147
154
|
return viaHeadless(ctx, prompt, `briefing-ask:next-prompt:${job}:${crypto.createHash('sha1').update(String(key)).digest('hex').slice(0, 10)}`,
|
|
148
|
-
(why) => AG.audit(ctx.runtimeDir, { job, version: VERSION, run, event: 'run-error', error: String(why).slice(0, 160) })
|
|
155
|
+
(why) => AG.audit(ctx.runtimeDir, { job, version: VERSION, run, event: 'run-error', error: String(why).slice(0, 160) }),
|
|
156
|
+
{ timeoutMs: job === NP.JOB_SHARPEN ? SHARPEN_TIMEOUT_MS : TIMEOUT_MS });
|
|
149
157
|
}
|
|
150
158
|
|
|
151
159
|
/* ── what a card cites ───────────────────────────────────────────────────────────────────── */
|
|
@@ -285,4 +293,4 @@ async function handle(req, res, url, ctx) {
|
|
|
285
293
|
catch (e) { return send(bad(500, 'The suggestion failed: ' + String((e && e.message) || e).slice(0, 200))); }
|
|
286
294
|
}
|
|
287
295
|
|
|
288
|
-
module.exports = { prefixes: ['/api/next-prompt'], handle, VERSION, FIXTURE, MODELS, prefs, setPref, lineRef, replayCases, next, sharpen };
|
|
296
|
+
module.exports = { prefixes: ['/api/next-prompt'], handle, VERSION, FIXTURE, MODELS, LEAN_FLAGS, prefs, setPref, lineRef, replayCases, next, sharpen };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.521.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.521.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@anthropic-ai/sdk": "^1.3.0",
|
package/workspace/package.json
CHANGED
|
@@ -176,7 +176,7 @@ body.cz-home { min-height: 100vh; overflow: hidden; }
|
|
|
176
176
|
@media (min-width: 761px) {
|
|
177
177
|
/* the log scrolls at the panel's edge, not at the column's (PM, 2026-10-27 "Bad layout": the scrollbar stood in the middle of the page);
|
|
178
178
|
* its content and the header line keep to the same centred column as the box */
|
|
179
|
-
body.cz-home:not(.hm-vw-open) .hm-foot { box-sizing: border-box; width: 100%; max-width: 880px; align-self: center; }
|
|
179
|
+
body.cz-home:not(.hm-vw-open) .hm-foot, body.cz-home:not(.hm-vw-open) .np { box-sizing: border-box; width: 100%; max-width: 880px; align-self: center; }
|
|
180
180
|
body.cz-home:not(.hm-vw-open) .hm-log { box-sizing: border-box; width: 100%; padding-inline: max(0px, calc((100% - 880px) / 2)); }
|
|
181
181
|
body.cz-home:not(.hm-vw-open) .hm-chd { padding-left: max(0px, calc((100% - 880px) / 2)); }
|
|
182
182
|
@container (max-width: 968px) { .hm-reopen:not([hidden]) ~ .hm-log { padding-top: 46px; } }
|
|
@@ -810,13 +810,10 @@ form.hm-box textarea { min-height: 48px; border: 0; background: none; resize: no
|
|
|
810
810
|
.np-use { display: flex; align-items: flex-start; gap: 10px; flex: 1; min-width: 0; padding: 11px 0 11px 14px; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
|
|
811
811
|
.np-ib { flex: none; width: 36px; height: 36px; display: inline-grid; place-items: center; border: 0; border-radius: 999px; background: none; color: var(--cz-ink2); cursor: pointer; }
|
|
812
812
|
.np-ib:hover, .np-ib[aria-expanded="true"] { background: var(--cz-well); }
|
|
813
|
-
.np-spin { display: flex; align-items: center; gap:
|
|
813
|
+
.np-spin { display: flex; align-items: center; gap: 8px; padding: 2px 14px; font-size: var(--fs-meta); color: var(--cz-ink2); } /* quiet: one small line, not a card */
|
|
814
814
|
.np-sp { display: inline-flex; animation: np-rot 1.1s linear infinite; }
|
|
815
815
|
@keyframes np-rot { to { transform: rotate(360deg); } }
|
|
816
816
|
@media (prefers-reduced-motion: reduce) { .np-sp { animation: none; } }
|
|
817
|
-
.np-sk { flex: 1; display: flex; flex-direction: column; gap: 8px; }
|
|
818
|
-
.np-sk i { display: block; height: 12px; border-radius: 6px; background: var(--cz-bluebg); }
|
|
819
|
-
.np-sk i:first-child { width: 88%; } .np-sk i:last-child { width: 60%; }
|
|
820
817
|
.np-menu { position: absolute; right: 0; bottom: calc(100% + 8px); z-index: 20; width: 310px; max-width: calc(100vw - 32px); box-sizing: border-box; padding: 8px; border-radius: 20px;
|
|
821
818
|
background: var(--cz-card); box-shadow: 0 24px 60px -24px rgba(42,46,53,.45), 0 0 0 1px var(--cz-hair); display: flex; flex-direction: column; gap: 2px; }
|
|
822
819
|
.np-mh { margin: 0; padding: 8px 12px 4px; font-size: var(--fs-1); font-weight: 800; color: var(--cz-ink2); }
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* After a reply ends: up to two cards above the box, the one Claude recommends lit (white, blue ring, blue ✦), the other grey. A tap puts its
|
|
4
4
|
* text in the box; nothing sends, and the PM sends with the box's own ↑. No ↵, edit or dismiss icon. No card when there is no clear next step.
|
|
5
5
|
* The cards hide when the PM types or a new reply starts; they have no timeout.
|
|
6
|
-
* While the PM types a rough prompt (3 or more words), after a
|
|
6
|
+
* While the PM types a rough prompt (3 or more words), after a 2 s pause: one quiet line (a small turning mark, "Sharpening…") while Claude
|
|
7
|
+
* sharpens it, then one lit Sharper card. Typing is never held up; an answer for words no longer in the box, or one later than 30 s, is dropped.
|
|
7
8
|
* Its sliders icon opens the style menu: Sharpen as Polish, Prompt engineer (the default) or Your own, and the Sharpen as I type switch.
|
|
8
9
|
* After a tap on the Sharper card, ↶ in the box puts back what the PM wrote (NpB 5).
|
|
9
10
|
* Server: GET /api/next-prompt/prefs, POST /api/next-prompt/next and /sharpen (lib/routes-next-prompt.js, through lib/ai-guard.js); the style
|
|
@@ -15,7 +16,7 @@ const STAR = '<svg aria-hidden="true" width="15" height="15" viewBox="0 0 24 24"
|
|
|
15
16
|
const SLIDERS = 'M4 7h9M17 7h3M4 17h3M11 17h9M13 4v6M7 14v6', CHECK = 'M5 12l5 5L20 7', UNDO = 'M9 14L4 9l5-5M4 9h11a5 5 0 0 1 0 10h-3', SPIN = 'M12 3a9 9 0 1 1-9 9';
|
|
16
17
|
const ic = (d, w = 16) => `<svg aria-hidden="true" width="${w}" height="${w}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="${d}"/></svg>`;
|
|
17
18
|
const STYLES = [['polish', 'Polish', 'Clearer and shorter, your meaning kept'], ['engineer', 'Prompt engineer', 'Builds the best prompt for the job'], ['own', 'Your own', 'Your wording rules, written once in Settings']];
|
|
18
|
-
const PAUSE =
|
|
19
|
+
const PAUSE = 2000, WAIT = 30000;
|
|
19
20
|
export const roughText = (t) => { const s = String(t || '').trim(); return !s.startsWith('/') && s.length >= 12 && s.split(/\s+/).filter(Boolean).length >= 3; };
|
|
20
21
|
|
|
21
22
|
export function nextPromptUi({ el, host, box, redrawFoot }) {
|
|
@@ -36,7 +37,7 @@ export function nextPromptUi({ el, host, box, redrawFoot }) {
|
|
|
36
37
|
if (N.mode === 'cards' && N.cards.length) {
|
|
37
38
|
html = `<div class="np-list" role="group" aria-label="Suggested next prompts">${N.cards.map((c, i) => `<button type="button" class="np-card${c.rec ? ' rec' : ''}" data-np-use="${i}" aria-label="Use this prompt: ${esc(c.text)}"${c.rec ? ' title="Claude recommends this one"' : ''}>${STAR}<span class="np-t">${esc(c.text)}</span></button>`).join('')}</div>`;
|
|
38
39
|
} else if (N.mode === 'spin') {
|
|
39
|
-
html = `<div class="np-spin" role="status"
|
|
40
|
+
html = `<div class="np-spin" role="status"><span class="np-sp">${ic(SPIN, 14)}</span><span>Sharpening…</span></div>`;
|
|
40
41
|
} else if (N.mode === 'sharp' && N.sharp) {
|
|
41
42
|
const P = N.prefs || {}, st = P.style || 'engineer';
|
|
42
43
|
const menu = N.menu ? `<div class="np-menu" role="menu" aria-label="Style of sharper prompts" data-esc-layer><p class="np-mh">Sharpen as</p>${STYLES.map(([k, l, d]) => `<button type="button" role="menuitemradio" aria-checked="${st === k}" class="np-mi" data-np-voice="${k}"><span class="np-ck">${st === k ? ic(CHECK) : ''}</span><span><b>${esc(l)}</b><em>${esc(k === 'own' && !P.rules ? 'Write your rules in Settings first' : d)}</em></span></button>`).join('')}`
|
|
@@ -58,7 +59,9 @@ export function nextPromptUi({ el, host, box, redrawFoot }) {
|
|
|
58
59
|
const my = ++N.seq;
|
|
59
60
|
N.mode = 'spin'; N.sharp = null; draw();
|
|
60
61
|
const c = N.ctx || {};
|
|
62
|
+
const late = setTimeout(() => { if (my === N.seq && N.mode === 'spin') { N.seq++; N.mode = null; draw(); } }, WAIT); // no answer in time: the line goes quietly
|
|
61
63
|
const r = await host.pmPost('/api/next-prompt/sharpen', { key: c.key || '', text: value, reply: c.reply || '', initiative: c.initiative || '', title: c.title || '' }).catch(() => null);
|
|
64
|
+
clearTimeout(late);
|
|
62
65
|
const ta = box();
|
|
63
66
|
if (my !== N.seq || !ta || ta.value !== value) return; // the PM typed on, or sent: this answer is for words no longer in the box
|
|
64
67
|
if (r && r.ok && r.text) { N.mode = 'sharp'; N.sharp = r.text; } else { N.mode = null; }
|