create-pmos 0.154.0 → 0.156.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/README.md +18 -1
- package/bin/cockpit-install.js +10 -5
- package/install.ps1 +171 -0
- package/install.sh +184 -0
- package/package.json +3 -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/bin/pmos.js +1 -0
- package/workspace/doctor.js +4 -2
- package/workspace/guide/document-sheet.md +18 -3
- package/workspace/guide/launcher.md +2 -2
- package/workspace/lib/launcher-cli.js +2 -2
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/c3-demo-boards.js +4 -3
- package/workspace/public/pm/c3-demo-init.js +16 -0
- package/workspace/public/pm/c3-doc-sheet.js +328 -21
- package/workspace/public/pm/c3-docs.js +2 -1
- package/workspace/public/pm/c3-ib-docs.js +3 -1
- package/workspace/public/pm/c3-settings.js +1 -1
- package/workspace/public/pm/console3.css +83 -0
- package/workspace/public/pm/frame-copy.js +1 -1
- package/workspace/server.js +3 -3
- package/workspace/test-lists/c3-doc-sheet-v2.json +6 -0
package/README.md
CHANGED
|
@@ -20,6 +20,22 @@ You never need to write code. You need the courage to say what you want, and the
|
|
|
20
20
|
|
|
21
21
|
## Getting started (5 minutes)
|
|
22
22
|
|
|
23
|
+
**New computer, nothing installed?** Paste one line. It installs what is missing (Node.js, git, Claude Code), makes a `PMOS` folder in your home folder, adds the kit and opens the PMOS app. No Homebrew, no WSL, and no admin password on Windows. Run it again at any time: it skips what is already there.
|
|
24
|
+
|
|
25
|
+
Mac (Terminal):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
curl -fsSL https://unpkg.com/create-pmos/install.sh | bash
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Windows (PowerShell):
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
powershell -ExecutionPolicy Bypass -c "iex ((New-Object Net.WebClient).DownloadString('https://unpkg.com/create-pmos/install.ps1'))"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The Mac line also works on Linux. On a Mac without git, Apple's Command Line Tools window opens once: click Install. The first agent session in the app asks you to sign in to Claude.
|
|
38
|
+
|
|
23
39
|
**Have an existing project?**
|
|
24
40
|
|
|
25
41
|
```bash
|
|
@@ -98,7 +114,8 @@ Some machines lock down npm's global folder. If the activation prints a warning
|
|
|
98
114
|
|
|
99
115
|
### What to expect
|
|
100
116
|
|
|
101
|
-
- **Requirements:** Node 18+. macOS,
|
|
117
|
+
- **Requirements:** Node 18+. macOS, Windows 10 (1809) or later, and Linux; Windows runs natively, with no WSL. On macOS and Windows `node-pty` ships prebuilt; on Linux, activation builds it once with a C toolchain. `pmos doctor` names the exact fix when something is missing. Setup CI runs the one-line setup on clean macOS, Windows and Linux runners.
|
|
118
|
+
- **Not on Windows yet:** `pmos launcher on` (start at login), and meeting-notes transcription (its engine is Apple Silicon only).
|
|
102
119
|
- **It's local and private.** The server binds `127.0.0.1` and is never exposed to the network. Your agent CLI (Claude Code, Codex, a plain shell) runs **untouched** — its own login, slash commands, and keybindings all work; the workspace is the terminal, not a wrapper.
|
|
103
120
|
- **The gates are the same gates.** `approve` / `return` / `accept` in the GUI write the exact file-mode records (`state/acceptances.jsonl`) this kit already uses — only ever on your click. Nothing is recorded automatically.
|
|
104
121
|
- **Works with or without the kit.** Best in a repo you've run `create-pmos` in (it reads and writes `pmos/state/`), but it opens any repo — a plain project just records into the server's own state.
|
package/bin/cockpit-install.js
CHANGED
|
@@ -84,7 +84,8 @@ const lstat = (p) => { try { return fs.lstatSync(p); } catch { return null; } };
|
|
|
84
84
|
const real = (p) => { try { return fs.realpathSync(p); } catch { return null; } };
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
* Link `.claude/skills/<name>` to `pmos/skills-native/<name>` for every kit skill that has none
|
|
87
|
+
* Link `.claude/skills/<name>` to `pmos/skills-native/<name>` for every kit skill that has none
|
|
88
|
+
* (a relative symlink; a junction on Windows).
|
|
88
89
|
* @returns {{ linked: string[], ok: string[], blocked: {name:string, reason:string}[], badDescription: string[], errors: string[] }}
|
|
89
90
|
*/
|
|
90
91
|
function linkSkills(repoRoot) {
|
|
@@ -100,10 +101,14 @@ function linkSkills(repoRoot) {
|
|
|
100
101
|
if (!descriptionHasText(text)) { out.badDescription.push(e.name); continue; }
|
|
101
102
|
|
|
102
103
|
const link = path.join(dest, e.name);
|
|
103
|
-
|
|
104
|
+
// Windows refuses a directory symlink unless Developer Mode is on or the shell is elevated. A
|
|
105
|
+
// junction needs neither; it takes an absolute target. Everywhere else the link stays relative,
|
|
106
|
+
// so the repository can move.
|
|
107
|
+
const target = process.platform === 'win32' ? path.join(native, e.name) : `../../pmos/skills-native/${e.name}`;
|
|
108
|
+
const type = process.platform === 'win32' ? 'junction' : 'dir';
|
|
104
109
|
const st = lstat(link);
|
|
105
110
|
if (st === null) {
|
|
106
|
-
try { fs.mkdirSync(dest, { recursive: true }); fs.symlinkSync(target, link,
|
|
111
|
+
try { fs.mkdirSync(dest, { recursive: true }); fs.symlinkSync(target, link, type); out.linked.push(e.name); }
|
|
107
112
|
catch (err) { out.errors.push(`${e.name}: ${err.message}`); }
|
|
108
113
|
continue;
|
|
109
114
|
}
|
|
@@ -113,8 +118,8 @@ function linkSkills(repoRoot) {
|
|
|
113
118
|
if (r === null) {
|
|
114
119
|
let was = '';
|
|
115
120
|
try { was = fs.readlinkSync(link); } catch { /* keep blocked */ }
|
|
116
|
-
if (/(
|
|
117
|
-
try { fs.unlinkSync(link); fs.symlinkSync(target, link,
|
|
121
|
+
if (/(^|[\\/])pmos[\\/]skills-native[\\/]/.test(was)) {
|
|
122
|
+
try { fs.unlinkSync(link); fs.symlinkSync(target, link, type); out.linked.push(e.name); }
|
|
118
123
|
catch (err) { out.errors.push(`${e.name}: ${err.message}`); }
|
|
119
124
|
continue;
|
|
120
125
|
}
|
package/install.ps1
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# PMOS one-line setup for Windows (initiative one-line-setup).
|
|
2
|
+
#
|
|
3
|
+
# powershell -ExecutionPolicy Bypass -c "iex ((New-Object Net.WebClient).DownloadString('https://unpkg.com/create-pmos/install.ps1'))"
|
|
4
|
+
#
|
|
5
|
+
# Installs what is missing, makes a PMOS folder and opens the PMOS app. No admin rights, no WSL:
|
|
6
|
+
# 1. Node.js latest LTS from nodejs.org, into %LOCALAPPDATA%\PMOS\node
|
|
7
|
+
# 2. git the official Portable Git, into %LOCALAPPDATA%\PMOS\git (Claude Code needs its bash)
|
|
8
|
+
# 3. Claude Code its own installer, claude.ai/install.ps1
|
|
9
|
+
# 4. the PMOS kit in %USERPROFILE%\PMOS (npx create-pmos)
|
|
10
|
+
# 5. the PMOS app (npx create-pmos workspace), which opens in the browser
|
|
11
|
+
# Every step checks first and skips what is already there, so running the line again is safe.
|
|
12
|
+
#
|
|
13
|
+
# Settings (environment variables):
|
|
14
|
+
# PMOS_DIR=<folder> the PMOS folder (default: %USERPROFILE%\PMOS)
|
|
15
|
+
# PMOS_NO_OPEN=1 do not open the browser at the end
|
|
16
|
+
# PMOS_SKIP_CLAUDE=1 do not install Claude Code
|
|
17
|
+
# PMOS_PKG=<spec> the create-pmos package to run (default: create-pmos@latest)
|
|
18
|
+
|
|
19
|
+
$ErrorActionPreference = 'Stop'
|
|
20
|
+
$ProgressPreference = 'SilentlyContinue' # Invoke-WebRequest is many times slower with the progress bar on
|
|
21
|
+
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
|
|
22
|
+
|
|
23
|
+
$PmosHome = Join-Path $env:LOCALAPPDATA 'PMOS'
|
|
24
|
+
$PmosDir = if ($env:PMOS_DIR) { $env:PMOS_DIR } else { Join-Path $env:USERPROFILE 'PMOS' }
|
|
25
|
+
$PmosPkg = if ($env:PMOS_PKG) { $env:PMOS_PKG } else { 'create-pmos@latest' }
|
|
26
|
+
$NodeMin = 18
|
|
27
|
+
|
|
28
|
+
function Say($m) { Write-Host "==> $m" -ForegroundColor Cyan }
|
|
29
|
+
function Ok($m) { Write-Host " [ok] $m" -ForegroundColor Green }
|
|
30
|
+
function Fail($m, $try) {
|
|
31
|
+
Write-Host ""
|
|
32
|
+
Write-Host "x $m" -ForegroundColor Red
|
|
33
|
+
if ($try) { Write-Host " Try: $try" }
|
|
34
|
+
# A pasted line runs inside the user's own PowerShell window: throw instead of exit, so the window stays open.
|
|
35
|
+
throw "PMOS setup stopped: $m"
|
|
36
|
+
}
|
|
37
|
+
function Have($c) { [bool](Get-Command $c -ErrorAction SilentlyContinue) }
|
|
38
|
+
|
|
39
|
+
# Put a folder on PATH now and for new windows (the user's own PATH; no admin needed).
|
|
40
|
+
function Add-Path($dir) {
|
|
41
|
+
if (-not (($env:Path -split ';') -contains $dir)) { $env:Path = "$dir;$env:Path" }
|
|
42
|
+
$user = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
43
|
+
if (-not $user) { $user = '' }
|
|
44
|
+
if (-not (($user -split ';') -contains $dir)) {
|
|
45
|
+
[Environment]::SetEnvironmentVariable('Path', ($dir + ';' + $user).TrimEnd(';'), 'User')
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function Get-File($url, $out) {
|
|
49
|
+
try { Invoke-WebRequest -Uri $url -OutFile $out -UseBasicParsing }
|
|
50
|
+
catch { Fail "Could not download $url" 'check the internet connection, then paste the line again' }
|
|
51
|
+
}
|
|
52
|
+
function Node-Major {
|
|
53
|
+
try { [int]((& node -p "process.versions.node.split('.')[0]") 2>$null) } catch { 0 }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
$arch = if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64' -or $env:PROCESSOR_ARCHITEW6432 -eq 'ARM64') { 'arm64' } else { 'x64' }
|
|
57
|
+
New-Item -ItemType Directory -Force -Path $PmosHome | Out-Null
|
|
58
|
+
|
|
59
|
+
Write-Host ""
|
|
60
|
+
Write-Host "PMOS setup - this installs what is missing and opens PMOS."
|
|
61
|
+
Write-Host ""
|
|
62
|
+
|
|
63
|
+
# --- 1. Node.js ---
|
|
64
|
+
Say 'Checking Node.js'
|
|
65
|
+
$nodeDir = Join-Path $PmosHome 'node'
|
|
66
|
+
if (Test-Path (Join-Path $nodeDir 'node.exe')) { Add-Path $nodeDir }
|
|
67
|
+
if ((Have node) -and ((Node-Major) -ge $NodeMin)) {
|
|
68
|
+
Ok "Node.js $(& node -v) is installed"
|
|
69
|
+
} else {
|
|
70
|
+
Say "Installing Node.js (latest LTS) into $nodeDir"
|
|
71
|
+
try { $index = Invoke-RestMethod -Uri 'https://nodejs.org/dist/index.json' -UseBasicParsing }
|
|
72
|
+
catch { Fail 'Could not read the Node.js version list from nodejs.org.' 'check the internet connection, then paste the line again' }
|
|
73
|
+
$ver = ($index | Where-Object { $_.lts } | Select-Object -First 1).version
|
|
74
|
+
$zip = Join-Path $env:TEMP "node-$ver-win-$arch.zip"
|
|
75
|
+
Get-File "https://nodejs.org/dist/$ver/node-$ver-win-$arch.zip" $zip
|
|
76
|
+
$unz = Join-Path $env:TEMP "pmos-node-$ver"
|
|
77
|
+
if (Test-Path $unz) { Remove-Item -Recurse -Force $unz }
|
|
78
|
+
Expand-Archive -Path $zip -DestinationPath $unz -Force
|
|
79
|
+
if (Test-Path $nodeDir) { Remove-Item -Recurse -Force $nodeDir }
|
|
80
|
+
Move-Item -Path (Join-Path $unz "node-$ver-win-$arch") -Destination $nodeDir
|
|
81
|
+
Remove-Item -Force $zip; Remove-Item -Recurse -Force $unz -ErrorAction SilentlyContinue
|
|
82
|
+
Add-Path $nodeDir
|
|
83
|
+
Ok "Node.js $(& node -v) installed"
|
|
84
|
+
}
|
|
85
|
+
# Windows blocks PowerShell scripts by default, and npm's `pmos` and `npx` commands are partly scripts.
|
|
86
|
+
# RemoteSigned for this user only (no admin) is the setting npm's own docs give.
|
|
87
|
+
$policy = Get-ExecutionPolicy -Scope CurrentUser
|
|
88
|
+
if ($policy -eq 'Undefined' -or $policy -eq 'Restricted') {
|
|
89
|
+
try { Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force; Ok 'PowerShell may now run the pmos command (RemoteSigned, this user only)' }
|
|
90
|
+
catch { Write-Host " note: could not change the PowerShell script setting; type pmos.cmd instead of pmos" }
|
|
91
|
+
}
|
|
92
|
+
# npm's global folder is where `pmos` lands. It must exist and be on PATH. It is %APPDATA%\npm unless
|
|
93
|
+
# npm is set up otherwise, so ask npm.
|
|
94
|
+
New-Item -ItemType Directory -Force -Path (Join-Path $env:APPDATA 'npm') | Out-Null
|
|
95
|
+
$npmGlobal = ((& npm.cmd prefix -g) | Select-Object -Last 1).Trim()
|
|
96
|
+
if (-not $npmGlobal) { $npmGlobal = Join-Path $env:APPDATA 'npm' }
|
|
97
|
+
New-Item -ItemType Directory -Force -Path $npmGlobal | Out-Null
|
|
98
|
+
Add-Path $npmGlobal
|
|
99
|
+
|
|
100
|
+
# --- 2. git ---
|
|
101
|
+
Say 'Checking git'
|
|
102
|
+
$gitDir = Join-Path $PmosHome 'git'
|
|
103
|
+
if (Test-Path (Join-Path $gitDir 'cmd\git.exe')) { Add-Path (Join-Path $gitDir 'cmd') }
|
|
104
|
+
if (Have git) {
|
|
105
|
+
Ok (& git --version)
|
|
106
|
+
} else {
|
|
107
|
+
Say "Installing Portable Git into $gitDir"
|
|
108
|
+
try {
|
|
109
|
+
$h = @{ 'User-Agent' = 'pmos-setup' }
|
|
110
|
+
if ($env:GITHUB_TOKEN) { $h['Authorization'] = "Bearer $env:GITHUB_TOKEN" } # CI only: the anonymous rate limit is shared per address
|
|
111
|
+
$rel = Invoke-RestMethod -Uri 'https://api.github.com/repos/git-for-windows/git/releases/latest' -UseBasicParsing -Headers $h
|
|
112
|
+
} catch { Fail 'Could not read the Git for Windows release list from GitHub.' 'check the internet connection, then paste the line again' }
|
|
113
|
+
$want = if ($arch -eq 'arm64') { 'PortableGit-*-arm64.7z.exe' } else { 'PortableGit-*-64-bit.7z.exe' }
|
|
114
|
+
$asset = $rel.assets | Where-Object { $_.name -like $want } | Select-Object -First 1
|
|
115
|
+
if (-not $asset) { Fail 'Could not find Portable Git for this computer.' 'install Git from https://git-scm.com/download/win, then paste the line again' }
|
|
116
|
+
$exe = Join-Path $env:TEMP $asset.name
|
|
117
|
+
Get-File $asset.browser_download_url $exe
|
|
118
|
+
if (Test-Path $gitDir) { Remove-Item -Recurse -Force $gitDir }
|
|
119
|
+
# A self-extracting 7-Zip archive: -o is the target folder, -y answers yes. No installer, no admin.
|
|
120
|
+
$p = Start-Process -FilePath $exe -ArgumentList "-o`"$gitDir`"", '-y' -Wait -PassThru -WindowStyle Hidden
|
|
121
|
+
Remove-Item -Force $exe -ErrorAction SilentlyContinue
|
|
122
|
+
if ($p.ExitCode -ne 0 -or -not (Test-Path (Join-Path $gitDir 'cmd\git.exe'))) { Fail 'Portable Git did not unpack.' 'paste the line again' }
|
|
123
|
+
Add-Path (Join-Path $gitDir 'cmd')
|
|
124
|
+
Ok (& git --version)
|
|
125
|
+
}
|
|
126
|
+
# Claude Code runs its commands in Git Bash. Tell it where bash is when git came from Portable Git.
|
|
127
|
+
$bash = Join-Path $gitDir 'bin\bash.exe'
|
|
128
|
+
if ((Test-Path $bash) -and -not $env:CLAUDE_CODE_GIT_BASH_PATH) {
|
|
129
|
+
[Environment]::SetEnvironmentVariable('CLAUDE_CODE_GIT_BASH_PATH', $bash, 'User')
|
|
130
|
+
$env:CLAUDE_CODE_GIT_BASH_PATH = $bash
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# --- 3. Claude Code ---
|
|
134
|
+
Say 'Checking Claude Code'
|
|
135
|
+
$claudeBin = Join-Path $env:USERPROFILE '.local\bin'
|
|
136
|
+
if (Test-Path (Join-Path $claudeBin 'claude.exe')) { Add-Path $claudeBin }
|
|
137
|
+
if ($env:PMOS_SKIP_CLAUDE -eq '1') {
|
|
138
|
+
Ok 'skipped (PMOS_SKIP_CLAUDE=1)'
|
|
139
|
+
} elseif (Have claude) {
|
|
140
|
+
Ok 'Claude Code is installed'
|
|
141
|
+
} else {
|
|
142
|
+
Say 'Installing Claude Code'
|
|
143
|
+
# A child PowerShell: the Claude Code installer may end its own session, which must not end this one.
|
|
144
|
+
& powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://claude.ai/install.ps1 | iex"
|
|
145
|
+
if (Test-Path (Join-Path $claudeBin 'claude.exe')) { Add-Path $claudeBin }
|
|
146
|
+
if (Have claude) { Ok 'Claude Code installed' }
|
|
147
|
+
else { Fail 'Claude Code did not install.' 'run: irm https://claude.ai/install.ps1 | iex then paste the line again' }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# --- 4. The PMOS folder and kit ---
|
|
151
|
+
Say "Setting up the PMOS folder: $PmosDir"
|
|
152
|
+
New-Item -ItemType Directory -Force -Path $PmosDir | Out-Null
|
|
153
|
+
if (-not (Test-Path (Join-Path $PmosDir '.git'))) {
|
|
154
|
+
& git -C $PmosDir init -q
|
|
155
|
+
Ok 'git folder created'
|
|
156
|
+
}
|
|
157
|
+
if (Test-Path (Join-Path $PmosDir 'pmos\AGENTS.md')) {
|
|
158
|
+
Ok 'the PMOS kit is already in this folder'
|
|
159
|
+
} else {
|
|
160
|
+
& npx.cmd -y -p $PmosPkg create-pmos $PmosDir
|
|
161
|
+
if ($LASTEXITCODE -ne 0) { Fail 'Could not add the PMOS kit.' 'paste the line again; if it fails again, send the text above to your PMOS contact' }
|
|
162
|
+
Ok 'PMOS kit added'
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# --- 5. The PMOS app ---
|
|
166
|
+
Say 'Starting the PMOS app'
|
|
167
|
+
& npx.cmd -y -p $PmosPkg create-pmos workspace $PmosDir
|
|
168
|
+
if ($LASTEXITCODE -ne 0) { Fail 'The PMOS app did not start.' 'run: pmos doctor and send what it prints to your PMOS contact' }
|
|
169
|
+
|
|
170
|
+
Write-Host ""
|
|
171
|
+
Ok 'PMOS is ready. Next time, open PowerShell and type: pmos'
|
package/install.sh
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# PMOS one-line setup for macOS and Linux (initiative one-line-setup).
|
|
3
|
+
#
|
|
4
|
+
# curl -fsSL https://unpkg.com/create-pmos/install.sh | bash
|
|
5
|
+
#
|
|
6
|
+
# Installs what is missing, makes a PMOS folder and opens the PMOS app:
|
|
7
|
+
# 1. Node.js (latest LTS from nodejs.org, into ~/.pmos/node; no Homebrew, no admin)
|
|
8
|
+
# 2. git (macOS: Apple's Command Line Tools, one system dialog; Linux: the package manager)
|
|
9
|
+
# 3. Claude Code (its own installer, claude.ai/install.sh)
|
|
10
|
+
# 4. the PMOS kit in ~/PMOS (npx create-pmos)
|
|
11
|
+
# 5. the PMOS app (npx create-pmos workspace), which opens in the browser
|
|
12
|
+
# Every step checks first and skips what is already there, so running the line again is safe.
|
|
13
|
+
#
|
|
14
|
+
# Settings (environment variables):
|
|
15
|
+
# PMOS_DIR=<folder> the PMOS folder (default: ~/PMOS)
|
|
16
|
+
# PMOS_NO_OPEN=1 do not open the browser at the end
|
|
17
|
+
# PMOS_SKIP_CLAUDE=1 do not install Claude Code
|
|
18
|
+
# PMOS_PKG=<spec> the create-pmos package to run (default: create-pmos@latest)
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
PMOS_HOME="${PMOS_HOME:-$HOME/.pmos}"
|
|
22
|
+
PMOS_DIR="${PMOS_DIR:-$HOME/PMOS}"
|
|
23
|
+
PMOS_PKG="${PMOS_PKG:-create-pmos@latest}"
|
|
24
|
+
NODE_MIN=18
|
|
25
|
+
MARK="# added by PMOS setup"
|
|
26
|
+
|
|
27
|
+
say() { printf '\033[1m==>\033[0m %s\n' "$*"; }
|
|
28
|
+
ok() { printf ' \033[32m✓\033[0m %s\n' "$*"; }
|
|
29
|
+
fail() { printf '\n\033[31m✗ %s\033[0m\n' "$1" >&2; [ -n "${2:-}" ] && printf ' Try: %s\n' "$2" >&2; exit 1; }
|
|
30
|
+
have() { command -v "$1" >/dev/null 2>&1; }
|
|
31
|
+
|
|
32
|
+
OS="$(uname -s)"
|
|
33
|
+
case "$OS" in
|
|
34
|
+
Darwin) PLAT=darwin ;;
|
|
35
|
+
Linux) PLAT=linux ;;
|
|
36
|
+
*) fail "This setup is for macOS and Linux. On Windows, paste the PowerShell line instead." \
|
|
37
|
+
"powershell -ExecutionPolicy Bypass -c \"iex ((New-Object Net.WebClient).DownloadString('https://unpkg.com/create-pmos/install.ps1'))\"" ;;
|
|
38
|
+
esac
|
|
39
|
+
case "$(uname -m)" in
|
|
40
|
+
arm64|aarch64) ARCH=arm64 ;;
|
|
41
|
+
x86_64|amd64) ARCH=x64 ;;
|
|
42
|
+
*) fail "This computer's processor ($(uname -m)) is not supported." ;;
|
|
43
|
+
esac
|
|
44
|
+
|
|
45
|
+
# The shell start-up files that new Terminal windows read, so PATH changes last. On Linux a new window
|
|
46
|
+
# reads ~/.bashrc and a login reads ~/.bash_profile or ~/.profile, so both get the line.
|
|
47
|
+
profile_files() {
|
|
48
|
+
case "${SHELL:-}" in
|
|
49
|
+
*/zsh) echo "$HOME/.zprofile" ;;
|
|
50
|
+
*/bash) if [ "$PLAT" = darwin ]; then echo "$HOME/.bash_profile"
|
|
51
|
+
else
|
|
52
|
+
echo "$HOME/.bashrc"
|
|
53
|
+
# A login shell reads ~/.bash_profile instead of ~/.profile when it exists.
|
|
54
|
+
if [ -f "$HOME/.bash_profile" ]; then echo "$HOME/.bash_profile"; else echo "$HOME/.profile"; fi
|
|
55
|
+
fi ;;
|
|
56
|
+
*) echo "$HOME/.profile" ;;
|
|
57
|
+
esac
|
|
58
|
+
}
|
|
59
|
+
# Put a folder on PATH now and in new Terminal windows. Adds the line once per file.
|
|
60
|
+
add_path() {
|
|
61
|
+
local dir="$1" f
|
|
62
|
+
case ":$PATH:" in *":$dir:"*) ;; *) export PATH="$dir:$PATH" ;; esac
|
|
63
|
+
for f in $(profile_files); do
|
|
64
|
+
touch "$f"
|
|
65
|
+
grep -qsF "$dir" "$f" || printf '\nexport PATH="%s:$PATH" %s\n' "$dir" "$MARK" >> "$f"
|
|
66
|
+
done
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
node_major() { node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0; }
|
|
70
|
+
|
|
71
|
+
echo
|
|
72
|
+
echo "PMOS setup — this installs what is missing and opens PMOS."
|
|
73
|
+
echo
|
|
74
|
+
|
|
75
|
+
# ── 1. Node.js ────────────────────────────────────────────────────────────────────────────────
|
|
76
|
+
say "Checking Node.js"
|
|
77
|
+
[ -x "$PMOS_HOME/node/bin/node" ] && add_path "$PMOS_HOME/node/bin"
|
|
78
|
+
if have node && [ "$(node_major)" -ge "$NODE_MIN" ]; then
|
|
79
|
+
ok "Node.js $(node -v) is installed"
|
|
80
|
+
else
|
|
81
|
+
say "Installing Node.js (latest LTS) into $PMOS_HOME/node"
|
|
82
|
+
have curl || fail "curl is missing." "install curl, then paste the line again"
|
|
83
|
+
# The list is newest first; the first entry whose "lts" is not false is the latest LTS. awk reads to the end
|
|
84
|
+
# (no early exit), so nothing upstream is cut off mid-write under pipefail.
|
|
85
|
+
ver="$(curl -fsSL https://nodejs.org/dist/index.json | tr '{' '\n' \
|
|
86
|
+
| awk '!found && /"version":"v/ && !/"lts":false/ { sub(/.*"version":"/, ""); sub(/".*/, ""); print; found = 1 }')"
|
|
87
|
+
[ -n "$ver" ] || fail "Could not read the Node.js version list from nodejs.org." "check the internet connection, then paste the line again"
|
|
88
|
+
tmp="$(mktemp -d)"
|
|
89
|
+
curl -fsSL "https://nodejs.org/dist/$ver/node-$ver-$PLAT-$ARCH.tar.gz" -o "$tmp/node.tar.gz" \
|
|
90
|
+
|| fail "Could not download Node.js $ver." "check the internet connection, then paste the line again"
|
|
91
|
+
rm -rf "$PMOS_HOME/node" && mkdir -p "$PMOS_HOME/node"
|
|
92
|
+
tar -xzf "$tmp/node.tar.gz" -C "$PMOS_HOME/node" --strip-components=1 --no-same-owner
|
|
93
|
+
rm -rf "$tmp"
|
|
94
|
+
add_path "$PMOS_HOME/node/bin"
|
|
95
|
+
hash -r
|
|
96
|
+
ok "Node.js $(node -v) installed"
|
|
97
|
+
fi
|
|
98
|
+
# A system Node whose global folder needs sudo: point npm's global folder at one the user owns.
|
|
99
|
+
npm_prefix="$(npm prefix -g 2>/dev/null || true)"
|
|
100
|
+
if [ -n "$npm_prefix" ] && [ ! -w "$npm_prefix/lib" ] && [ ! -w "$npm_prefix" ]; then
|
|
101
|
+
mkdir -p "$PMOS_HOME/npm-global"
|
|
102
|
+
npm config set prefix "$PMOS_HOME/npm-global"
|
|
103
|
+
add_path "$PMOS_HOME/npm-global/bin"
|
|
104
|
+
ok "npm global folder set to $PMOS_HOME/npm-global (no sudo needed)"
|
|
105
|
+
fi
|
|
106
|
+
# npm's global folder is where `pmos` lands: put its bin folder on PATH, wherever npm is set up to keep it.
|
|
107
|
+
npm_prefix="$(npm prefix -g 2>/dev/null || true)"
|
|
108
|
+
[ -n "$npm_prefix" ] && [ -d "$npm_prefix" ] && add_path "$npm_prefix/bin"
|
|
109
|
+
|
|
110
|
+
# ── 2. git ────────────────────────────────────────────────────────────────────────────────────
|
|
111
|
+
say "Checking git"
|
|
112
|
+
if [ "$PLAT" = darwin ]; then
|
|
113
|
+
if xcode-select -p >/dev/null 2>&1 && git --version >/dev/null 2>&1; then
|
|
114
|
+
ok "$(git --version)"
|
|
115
|
+
else
|
|
116
|
+
say "Installing Apple's Command Line Tools (they include git)"
|
|
117
|
+
echo " A window opens. Click Install and wait for it to finish. This step can take a few minutes."
|
|
118
|
+
xcode-select --install >/dev/null 2>&1 || true
|
|
119
|
+
waited=0
|
|
120
|
+
until xcode-select -p >/dev/null 2>&1; do
|
|
121
|
+
sleep 5; waited=$((waited + 5))
|
|
122
|
+
[ "$waited" -ge 3600 ] && fail "The Command Line Tools did not finish installing." "run: xcode-select --install, then paste the line again"
|
|
123
|
+
done
|
|
124
|
+
ok "$(git --version)"
|
|
125
|
+
fi
|
|
126
|
+
else
|
|
127
|
+
if have git; then
|
|
128
|
+
ok "$(git --version)"
|
|
129
|
+
else
|
|
130
|
+
say "Installing git (your password may be asked)"
|
|
131
|
+
if have apt-get; then sudo apt-get update -qq && sudo apt-get install -y -qq git
|
|
132
|
+
elif have dnf; then sudo dnf install -y -q git
|
|
133
|
+
elif have pacman; then sudo pacman -S --noconfirm git
|
|
134
|
+
else fail "Could not install git automatically." "install git with your package manager, then paste the line again"; fi
|
|
135
|
+
ok "$(git --version)"
|
|
136
|
+
fi
|
|
137
|
+
# On Linux the terminal component (node-pty) is built from source: it needs a compiler and Python.
|
|
138
|
+
if ! have make || ! have g++ || ! have python3; then
|
|
139
|
+
say "Installing build tools for the PMOS app (your password may be asked)"
|
|
140
|
+
if have apt-get; then sudo apt-get install -y -qq build-essential python3
|
|
141
|
+
elif have dnf; then sudo dnf install -y -q gcc-c++ make python3
|
|
142
|
+
elif have pacman; then sudo pacman -S --noconfirm base-devel python
|
|
143
|
+
else fail "Could not install build tools automatically." "install a C++ compiler, make and python3, then paste the line again"; fi
|
|
144
|
+
ok "build tools installed"
|
|
145
|
+
fi
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
# ── 3. Claude Code ───────────────────────────────────────────────────────────────────────────
|
|
149
|
+
say "Checking Claude Code"
|
|
150
|
+
[ -x "$HOME/.local/bin/claude" ] && add_path "$HOME/.local/bin"
|
|
151
|
+
if [ "${PMOS_SKIP_CLAUDE:-}" = 1 ]; then
|
|
152
|
+
ok "skipped (PMOS_SKIP_CLAUDE=1)"
|
|
153
|
+
elif have claude; then
|
|
154
|
+
ok "Claude Code is installed"
|
|
155
|
+
else
|
|
156
|
+
say "Installing Claude Code"
|
|
157
|
+
curl -fsSL https://claude.ai/install.sh | bash \
|
|
158
|
+
|| fail "Could not install Claude Code." "run: curl -fsSL https://claude.ai/install.sh | bash, then paste the line again"
|
|
159
|
+
add_path "$HOME/.local/bin"
|
|
160
|
+
hash -r
|
|
161
|
+
have claude && ok "Claude Code installed" || fail "Claude Code installed, but the claude command is not found." "open a new Terminal window and paste the line again"
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# ── 4. The PMOS folder and kit ──────────────────────────────────────────────────────────────
|
|
165
|
+
say "Setting up the PMOS folder: $PMOS_DIR"
|
|
166
|
+
mkdir -p "$PMOS_DIR"
|
|
167
|
+
if [ ! -d "$PMOS_DIR/.git" ] && ! git -C "$PMOS_DIR" rev-parse --git-dir >/dev/null 2>&1; then
|
|
168
|
+
git -C "$PMOS_DIR" init -q
|
|
169
|
+
ok "git folder created"
|
|
170
|
+
fi
|
|
171
|
+
if [ -f "$PMOS_DIR/pmos/AGENTS.md" ]; then
|
|
172
|
+
ok "the PMOS kit is already in this folder"
|
|
173
|
+
else
|
|
174
|
+
npx -y -p "$PMOS_PKG" create-pmos "$PMOS_DIR" || fail "Could not add the PMOS kit." "paste the line again; if it fails again, send the text above to your PMOS contact"
|
|
175
|
+
ok "PMOS kit added"
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# ── 5. The PMOS app ─────────────────────────────────────────────────────────────────────────
|
|
179
|
+
say "Starting the PMOS app"
|
|
180
|
+
npx -y -p "$PMOS_PKG" create-pmos workspace "$PMOS_DIR" || fail "The PMOS app did not start." "run: pmos doctor, and send what it prints to your PMOS contact"
|
|
181
|
+
|
|
182
|
+
echo
|
|
183
|
+
ok "PMOS is ready. Next time, open Terminal and type: pmos"
|
|
184
|
+
echo " New Terminal windows find node, claude and pmos by themselves."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.156.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": {
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
"bin/",
|
|
11
11
|
"payload/",
|
|
12
12
|
"workspace/",
|
|
13
|
+
"install.sh",
|
|
14
|
+
"install.ps1",
|
|
13
15
|
"README.md",
|
|
14
16
|
"LICENSE"
|
|
15
17
|
],
|
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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-12T05:09:50Z"}
|
|
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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-12T05:09:50Z"}
|
|
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-
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-12T05:09:50Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
package/workspace/bin/pmos.js
CHANGED
|
@@ -329,6 +329,7 @@ async function up(dir, { remote = false, open = true } = {}) {
|
|
|
329
329
|
};
|
|
330
330
|
const child = spawn(process.execPath, [path.join(WS, 'server.js')], {
|
|
331
331
|
cwd: WS, detached: true, stdio: ['ignore', log, log], env,
|
|
332
|
+
windowsHide: true, // Windows: no console window for the background server
|
|
332
333
|
});
|
|
333
334
|
// A server that exits at once (a refused tailnet bind, a port lost to a race) ends the wait below early.
|
|
334
335
|
let exited = false;
|
package/workspace/doctor.js
CHANGED
|
@@ -33,7 +33,9 @@ function binaryArch(file) {
|
|
|
33
33
|
console.log(`pmos workspace doctor — node ${process.version} (${process.platform}/${process.arch})`);
|
|
34
34
|
console.log(` install dir: ${WSDIR}\n`);
|
|
35
35
|
|
|
36
|
-
const REINSTALL =
|
|
36
|
+
const REINSTALL = process.platform === 'win32'
|
|
37
|
+
? 'npx create-pmos@latest workspace --reinstall (reinstalls the app and node-pty for this computer)'
|
|
38
|
+
: `cd "${WSDIR}" && rm -rf node_modules && npm install (reinstalls node-pty for this platform)`;
|
|
37
39
|
|
|
38
40
|
// 1. Node version
|
|
39
41
|
const major = Number(process.versions.node.split('.')[0]);
|
|
@@ -101,7 +103,7 @@ function binaryArch(file) {
|
|
|
101
103
|
bad(`PTY spawn failed: ${e.message}`,
|
|
102
104
|
/posix_spawnp/.test(e.message)
|
|
103
105
|
? `on macOS this is node-pty's spawn-helper lacking +x (check 3 restores it — re-run \`npm run doctor\`); otherwise a wrong-arch binary — ${REINSTALL}`
|
|
104
|
-
: process.platform === 'win32' ?
|
|
106
|
+
: process.platform === 'win32' ? `Windows 10 version 1809 or later is needed for terminals (ConPTY); otherwise ${REINSTALL}`
|
|
105
107
|
: 'check that /bin/sh exists and is executable');
|
|
106
108
|
}
|
|
107
109
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: Guide
|
|
3
3
|
title: "The document sheet"
|
|
4
|
-
description: "How to read a document as rich text, edit it, review the agent's proposals section by section, read the edit log, and clear a stale document."
|
|
4
|
+
description: "How to read a document as rich text with its outline, edit it, review the agent's proposals section by section, read the edit log, keep its Confluence and Google Doc copies in step, see the documents around it, and clear a stale document."
|
|
5
5
|
timestamp: 2026-10-12
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -16,7 +16,8 @@ Read shows the document as formatted text: headings, bold, links, tables, checkl
|
|
|
16
16
|
- Each section shows who changed it last: a person, or the agent and the person who accepted it, with the revision and the day. Select the name to open that revision in the edit log.
|
|
17
17
|
- **Show who wrote what** colours each section: blue for a person, purple for the agent.
|
|
18
18
|
- **Markdown source** shows the file as text. **Copy the markdown** copies it.
|
|
19
|
-
- **
|
|
19
|
+
- The **outline** beside the text lists the headings, nested by level. The section you read is marked as you scroll. Select a heading to go to it, or the arrow beside a heading to fold the headings under it. A document with more than 12 headings has a **Find a heading** box. An orange dot marks a section that cites a source that changed. A purple dot marks a section that an open proposal changes.
|
|
20
|
+
- **Hide the outline** hides it, and the sheet keeps that choice in this browser. On a narrow screen the outline is a button, **Outline**, that names the section you read and opens the list over the text.
|
|
20
21
|
- When an open proposal changes a section, a note shows above that section. Select **Show change** to see the difference in place.
|
|
21
22
|
|
|
22
23
|
## Edit
|
|
@@ -53,6 +54,20 @@ A document is stale when a document that it links to changed after it. The sheet
|
|
|
53
54
|
- Edit the sections, or ask the agent for an update.
|
|
54
55
|
- If the document is still right, select **Mark as still current** and write why. The mark counts as a change, so the banner clears.
|
|
55
56
|
|
|
57
|
+
## Copies in Confluence and Google Docs
|
|
58
|
+
|
|
59
|
+
The header shows one chip for each copy of the document in Confluence or Google Docs, with its state: in step, behind (the copy has changes), PMOS ahead (PMOS has changes), both changed, or not checked. Select a chip to open **Copies in other tools**. Each copy shows its state, who changed it last and when, and when it was last checked.
|
|
60
|
+
|
|
61
|
+
- **Check now** starts an agent session that reads the copies with your connectors. The states change when it finishes.
|
|
62
|
+
- **Open in Confluence** or **Open in Google Doc** opens the copy.
|
|
63
|
+
- When the copy is behind or both changed, **Bring their change in as a proposal** asks the agent to read the copy and draft its changes as a proposal. Review it in Proposals, section by section. PMOS keeps a fingerprint of the copy, not its text, so the proposal is where you see the difference.
|
|
64
|
+
- When PMOS is ahead, **Update the Google Doc to this version** (or the Confluence page) starts an agent session that writes the copy. You have five seconds to select **Undo**.
|
|
65
|
+
- **Link a copy** takes the address of a Confluence page or a Google Doc. The sheet finds the tool from the address.
|
|
66
|
+
|
|
67
|
+
PMOS never overwrites the document from a copy.
|
|
68
|
+
|
|
56
69
|
## Health and Relations
|
|
57
70
|
|
|
58
|
-
Health shows the checks of the document and how many pass.
|
|
71
|
+
Health shows the checks of the document and how many pass.
|
|
72
|
+
|
|
73
|
+
Relations shows a graph of the documents around this one: the documents it uses on the left, its initiative and goals on top, and the documents that use it and its copies on the right. A dashed orange line shows a document that is out of step. Select a document to open it. The lists under the graph name the same documents. From Initiatives › Documents, **Open the full graph in Docs** shows the whole graph.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: Guide
|
|
3
3
|
title: "Start the workspace without a terminal"
|
|
4
|
-
timestamp: 2026-10-
|
|
4
|
+
timestamp: 2026-10-12
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Start the workspace without a terminal
|
|
@@ -47,5 +47,5 @@ Type `pmos launcher off`. It removes the app and the login item. It removes only
|
|
|
47
47
|
## Limits
|
|
48
48
|
|
|
49
49
|
- The login item never starts remote (tailnet) mode, and it writes no token. To open the workspace from your phone, start remote mode yourself as `workspace/REMOTE.md` tells you.
|
|
50
|
-
-
|
|
50
|
+
- Windows has no launcher yet. The workspace itself runs on Windows with no WSL: open PowerShell and type `pmos`. If PMOS is not installed yet, the one-line setup in the create-pmos README installs it.
|
|
51
51
|
- The login item runs once, at login. If the server stops, open the app to start it again.
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* A file started by the desktop has no shell profile, so the PATH of the terminal that ran `on` is written into the
|
|
17
17
|
* script; that is how `claude`, `git` and `tmux` resolve. It never starts remote (tailnet) mode and writes no token.
|
|
18
18
|
* `off` deletes a file only when it carries MARK, so a file of the same name that the PM wrote is left alone.
|
|
19
|
-
*
|
|
19
|
+
* Windows is refused: no Start-menu or login item is made there yet. Start the workspace with `pmos`.
|
|
20
20
|
*
|
|
21
21
|
* plan() is pure: every input is injected, so each platform's files are assertable from any machine.
|
|
22
22
|
*/
|
|
@@ -81,7 +81,7 @@ function desktop(entries) {
|
|
|
81
81
|
* `login: false` leaves the login item out.
|
|
82
82
|
*/
|
|
83
83
|
function plan({ platform, home, env = {}, node, bin, repo, login = true }) {
|
|
84
|
-
if (platform === 'win32') return { refused: '
|
|
84
|
+
if (platform === 'win32') return { refused: 'no launcher is made on Windows yet. Start the workspace with `pmos` in PowerShell.' };
|
|
85
85
|
if (platform !== 'darwin' && platform !== 'linux') return { refused: `no launcher is made for ${platform}. Start the workspace with \`pmos\`.` };
|
|
86
86
|
const log = path.join(stateRoot.defaultRoot({ env, platform, home }), 'launcher.log');
|
|
87
87
|
const body = script({ node, bin, repo, pathEnv: env.PATH || '/usr/bin:/bin', log });
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.156.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.156.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/workspace/package.json
CHANGED
|
@@ -584,14 +584,15 @@ function handleMore(S, method, path, p) {
|
|
|
584
584
|
return okb({ board: { name: 'Demo board' }, releases: S.releases.filter((x) => x.board === p.board).map((x) => ({ id: x.id, name: x.name, type: 'both', initiative_ids: x.scope.slice(), deploy_at: x.date + 'T11:00:00', release_at: x.date + 'T12:00:00', accepted_at: x.status === 'Released' ? x.date + 'T12:30:00' : null, gapFlags: [], tags: { detected: [], reason: null }, summary: [], status: x.status, board: x.board })) });
|
|
585
585
|
}
|
|
586
586
|
if (path === '/api/docs/library') {
|
|
587
|
-
const files = [];
|
|
587
|
+
const files = []; const edges = []; // the research informs the PRD; the rubric comes from the PRD (the document sheet's Relations graph)
|
|
588
588
|
for (const r of feat(p.board)) {
|
|
589
|
-
const h = hash(r.id); const slug = r.id.toLowerCase();
|
|
589
|
+
const h = hash(r.id); const slug = r.id.toLowerCase(); const at0 = files.length;
|
|
590
590
|
if (r.spec) files.push({ path: 'planning/ib/' + slug + '-prd.md', title: r.title + ' PRD', category: 'spec', date: D(-(h % 20) - 1), initiative: r.id });
|
|
591
591
|
files.push({ path: 'planning/ib/' + slug + '-research.md', title: r.title + ' research', category: 'res', date: D(-(h % 50) - 5), initiative: r.id });
|
|
592
592
|
if (catOfRow(S, r) === 'Do') files.push({ path: 'planning/ib/' + slug + '-rubric.md', title: r.title + ' rubric', category: 'eval', date: D(-(h % 9) - 1), initiative: r.id });
|
|
593
|
+
if (r.spec) { edges.push([at0 + 1, at0, 0]); if (files.length === at0 + 3) edges.push([at0, at0 + 2, 1]); }
|
|
593
594
|
}
|
|
594
|
-
return okb({ key: 'demo', files, edges: [
|
|
595
|
+
return okb({ key: 'demo', files, edges, kinds: ['informs', 'rubric from'], categories: [{ key: 'spec', name: 'Specs' }, { key: 'eval', name: 'Evals' }, { key: 'res', name: 'Research & plans' }], initiatives: {} });
|
|
595
596
|
}
|
|
596
597
|
return undefined;
|
|
597
598
|
}
|