@manhquy/flow-skill 0.1.0-rc.2 → 0.1.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/CHANGELOG.md CHANGED
@@ -2,7 +2,42 @@
2
2
 
3
3
  All notable changes to `@manhquy/flow-skill`. Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org/).
4
4
 
5
- ## [0.1.0-rc.2] — 2026-07-17 (not yet published — see release command below)
5
+ ## [0.1.0] — 2026-07-19 — GA (first stable, `latest`)
6
+
7
+ First stable release of the installer. Graduates the rc.1→rc.3 line after two live OIDC
8
+ publishes + independent verification; publishes to dist-tag **`latest`** so bare
9
+ `npx @manhquy/flow-skill` / `npm i` resolves it (no manual dist-tag promotion needed).
10
+
11
+ ### Changed
12
+ - Ships skill product **v0.24.0** (harness trust-align — honest `proof_source`, STRICT durable
13
+ modes, canonical harness skill; see root `CHANGELOG.md` `## 0.24.0`). rc.1–rc.3 shipped v0.22.0.
14
+ - No installer code change vs rc.3 — same CLI, detection, lock, and five install targets
15
+ (Claude, Codex, Antigravity, Cursor, universal `~/.agents/skills`).
16
+
17
+ ## [0.1.0-rc.3] — 2026-07-18
18
+
19
+ Ships the same skill product **v0.22.0** as rc.2, plus pipeline/UX fixes found after the first
20
+ OIDC publish + independent verification.
21
+
22
+ ### Fixed
23
+ - **Nightly registry health false-red:** `scripts/smoke.mjs` ran `npx` from inside
24
+ `npm-wrapper/`, so npx resolved the **local workspace package** instead of the registry
25
+ (`flow-skill: not found`) even when the published package was healthy. Smoke now uses an
26
+ empty temp cwd + `npx --package=… flow-skill`; nightly runs from `$RUNNER_TEMP`.
27
+ - **CI bash-suite Windows cancelled at 15m:** full 33-suite run needs ~20–25m on Windows Git
28
+ Bash (measured run `29630992330` — mid-`test_flow_eval` when cut). `timeout-minutes` is now
29
+ **30 on windows-latest**, 15 elsewhere. `tests/run_all.sh` prints per-suite `wall_s`.
30
+ - **`promote_to`:** early-exit before install/test (no full job burn); input description states
31
+ it is **manual-only** (OIDC cannot `npm dist-tag add` — E401 on run `29556075144`).
32
+
33
+ ### Added
34
+ - Dual-version UX: `--help` shows `flow-skill v<pkg> (ships skill v<skill>)`; JSONL `plan`
35
+ event gains additive `skillVersion`. Unit tests pin values from `package.json` + `SKILL.md`.
36
+
37
+ ### Changed
38
+ - `bin/cli.mjs` git mode `100755` (executable bit in tarball).
39
+
40
+ ## [0.1.0-rc.2] — 2026-07-17 — LIVE (published 2026-07-17, `latest` + `rc` both point here)
6
41
 
7
42
  Ships `skills/flow` v0.22.0 (concierge front-door + standalone native rituals — see the root
8
43
  repo's CHANGELOG.md `## 0.22.0` entry for the full skill-content changelog). This wrapper
@@ -68,10 +103,38 @@ two hardening items found while verifying it all.
68
103
  both `install.sh` and `install.ps1` carry the Antigravity restart/reload hint, and the old
69
104
  hardcoded Claude+Codex-only Done line is gone from both.
70
105
 
71
- ### Release command (operator-gated — not run by this change)
106
+ ### Fixed — CI publish guard (found on the actual first real run of this workflow)
107
+ - `publish-npm-wrapper.yml`'s `Guard — assert no NODE_AUTH_TOKEN present (OIDC only)` step
108
+ **hard-failed every run, including this release's first attempt** (run `29554226397`,
109
+ 2026-07-17). Root-caused via a temporary diagnostic step (added, run 3×, removed same
110
+ session): `NODE_AUTH_TOKEN` is unset right after `actions/checkout@v4` but becomes a real
111
+ short-lived OIDC-exchanged value the instant `actions/setup-node@v4` runs with
112
+ `registry-url` set — that's the current `actions/setup-node@v4` (a moving tag) doing its own
113
+ native npm OIDC handling, not a leaked static secret. The guard's 2026-07-12 design assumed
114
+ the older model where `npm publish` alone performed the OIDC exchange with nothing set
115
+ beforehand; it never matched real `actions/setup-node@v4` behavior and had a 0% pass rate
116
+ since the day it was written. **Removed** — see the workflow file's inline comment for the
117
+ full evidence chain.
118
+ - **npm Trusted Publisher was never actually bound** on npmjs.com for this package (confirmed:
119
+ first real publish attempt after the guard fix still 404'd — `PUT
120
+ .../@manhquy%2fflow-skill — Not found`). The 2026-07-12 runbook flagged this config as `TBD`
121
+ and it was never closed out. Configured now: `owner=manhquydev`, `repo=flow-skill`,
122
+ `workflow=publish-npm-wrapper.yml`, `environment=npm-publish`. `npm publish --provenance`
123
+ now succeeds via pure OIDC — confirmed live, SLSA attestation present.
124
+ - **`npm dist-tag add` does NOT work via OIDC** (confirmed live: `E401 Unable to authenticate`
125
+ even immediately after a successful OIDC-authenticated publish in the same job). npm has not
126
+ extended Trusted Publishing to this endpoint yet (matches
127
+ `plans/reports/researcher-260712-1006-npm-trusted-publisher-first-publish-edge-cases-report.md`
128
+ item #12, "OIDC support in development", no ship date). Promoting `0.1.0-rc.2` to `latest`
129
+ required the same manual Granular-Access-Token-with-Bypass-2FA flow used for the rc.1
130
+ bootstrap publish — see `RELEASE_CHECKLIST.md` for the exact steps and why this account
131
+ (created 2026-07-11, after npm's Sept-2025 TOTP-enrollment cutoff) has no other CLI-auth path
132
+ for write operations.
133
+
134
+ ### Release command (already run for this release)
72
135
  ```
73
- cd npm-wrapper && npm test # confirm green first
74
- git tag npm@0.1.0-rc.2 && git push --tags # triggers .github/workflows/publish-npm-wrapper.yml
136
+ cd npm-wrapper && npm test # confirmed green first
137
+ git tag npm@0.1.0-rc.2 && git push origin npm@0.1.0-rc.2 # triggered the workflow (after the guard fix + TP config above)
75
138
  ```
76
139
 
77
140
  ## [0.1.0-rc.1] — 2026-07-12
package/README.md CHANGED
@@ -10,13 +10,21 @@ One-command installer that copies the [flow](https://github.com/manhquydev/flow-
10
10
  ## Install
11
11
 
12
12
  ```
13
- # Pre-release channel (current — v0.1.0-rc.2)
13
+ # Pre-release channel (current — v0.1.0-rc.3, ships skill v0.22.0)
14
14
  npx @manhquy/flow-skill@rc
15
15
  ```
16
16
 
17
17
  An interactive prompt asks which agents to install to. Pick one or more, confirm, done.
18
18
 
19
- > **RC phase**: pin `@rc` (dist-tag) or a specific version like `@0.1.0-rc.2`. `npx @manhquy/flow-skill@0.1.x` will start working after stable `0.1.0` is published — semver ranges do **not** match pre-release tuples by default. See [SECURITY.md](./SECURITY.md).
19
+ > **RC phase**: pin `@rc` (dist-tag) or a specific version like `@0.1.0-rc.3`. `npx @manhquy/flow-skill@0.1.x` will start working after stable `0.1.0` is published — semver ranges do **not** match pre-release tuples by default. See [SECURITY.md](./SECURITY.md).
20
+ >
21
+ > **`npm i` alone is not enough.** `npm install @manhquy/flow-skill` only adds the installer
22
+ > package to `node_modules`. You must **run** it (`npx @manhquy/flow-skill@rc` or
23
+ > `npx flow-skill` after install) to copy the skill into agent homes.
24
+ >
25
+ > **Two version axes:** package `version` in this folder is the **installer** (e.g. `0.1.0-rc.3`).
26
+ > Skill product version lives in `skills/flow/SKILL.md` (`metadata.version`, e.g. `0.22.0`).
27
+ > `--help` and the JSONL `plan` event expose both (`version` + `skillVersion`).
20
28
 
21
29
  ## Non-interactive
22
30
 
@@ -58,7 +66,7 @@ line tells you exactly what to do for whichever targets you installed:
58
66
  - Claude Code: type `/flow`.
59
67
  - Codex CLI: type `$flow` (restart Codex once to load a new skill).
60
68
  - Antigravity: restart/reload the IDE (or restart `agy`) to load the new skill, then type `/flow`.
61
- - Agents home: restart/reload your tool if it does not auto-detect new skills.
69
+ - Agents home (`~/.agents/skills/`): restart/reload your tool if it does not auto-detect new skills.
62
70
  - Cursor: **install verified, live runner execution not yet independently confirmed** — Cursor
63
71
  has no headless CLI probe available for automated verification (unlike Antigravity's `agy -p`
64
72
  or Codex's `codex exec`); restart/reload Cursor after install and check the Agent panel for
@@ -86,6 +94,8 @@ rm -rf <project>/.claude/skills/flow
86
94
  - **Windows `EBUSY` / `EPERM` mid-install**: an agent (Claude Code, Codex, Antigravity IDE) is holding a file inside the destination. Close the agent and re-run. The installer already retries with 100/300/900 ms backoff before surfacing the error.
87
95
  - **Stale advisory lock**: a prior run crashed. The next run detects the dead PID and reclaims the lock automatically. If it does not (very rare — the recorded PID was recycled by another live process), delete `<parent-of-dest>/.flow-skill.installing.lock`.
88
96
  - **`No matching version found` on `@0.1.x`**: you are trying to install a pre-release version through a stable range. Use `@rc` (dist-tag) or an explicit `@0.1.0-rc.N` until stable `0.1.0` ships.
97
+ - **`No matching version found` on `@0.22.0`**: that is the **skill product** version, not the npm package version. Use `@rc` / `@0.1.0-rc.N` (installer) — skill content version is printed by `--help` as `ships skill v…`.
98
+ - **Installed package but agent has no `/flow`**: `npm i` does not run the installer. Run `npx @manhquy/flow-skill@rc` (or `npx flow-skill` if already installed) and restart the agent.
89
99
  - **Node too old** (`requires Node.js >=22.14.0`): upgrade with your preferred version manager (`nvm install 22`, `fnm install 22`, or Node's official installer). Node 20 reached end-of-life April 2026; npm OIDC Trusted Publishing needs npm >=11.5.1 which bundles with Node 22.14+.
90
100
 
91
101
  ## JSONL contract
@@ -93,7 +103,7 @@ rm -rf <project>/.claude/skills/flow
93
103
  `--json` streams one JSON object per line:
94
104
 
95
105
  ```jsonl
96
- {"event":"plan","version":"0.1.0-rc.1","dryRun":false,"scope":"global","targets":["claude","codex"]}
106
+ {"event":"plan","version":"0.1.0-rc.3","skillVersion":"0.22.0","dryRun":false,"scope":"global","targets":["claude","codex"]}
97
107
  {"event":"install:start","target":"claude","dests":["~/.claude/skills/flow"]}
98
108
  {"event":"install:done","target":"claude","dests":["~/.claude/skills/flow"],"result":"success","error":null,"warnings":[]}
99
109
  {"event":"summary","success":true,"total":2,"attempted":2,"installed":2,"failed":0,"skipped":0,"aborted":false}
@@ -105,7 +115,7 @@ rm -rf <project>/.claude/skills/flow
105
115
 
106
116
  | Event | Required fields | Notes |
107
117
  |---|---|---|
108
- | `plan` | `version` (string), `dryRun` (bool), `scope` (`global`\|`project`), `targets` (string[]) | Always the first event. |
118
+ | `plan` | `version` (string), `dryRun` (bool), `scope` (`global`\|`project`), `targets` (string[]), optional `skillVersion` (string\|null) | Always the first event. `version` = npm package; `skillVersion` = bundled `SKILL.md` product version. |
109
119
  | `install:start` | `target` (string), `dests` (string[]) | One per selected target. |
110
120
  | `install:done` | `target`, `dests`, `result` (`success`\|`failed`), `error` (string\|null), `warnings` (string[]) | Fired after each target. `error` is `null` on success. |
111
121
  | `summary` | `success` (bool), `total`, `attempted`, `installed`, `failed`, `skipped`, `aborted` (bool) | Always the last event. Counts sum: `attempted = installed + failed`; `skipped = total - attempted`. |
package/README_VN.md CHANGED
@@ -10,13 +10,15 @@ Trình cài đặt một-lệnh sao chép skill [flow](https://github.com/manhqu
10
10
  ## Cài đặt
11
11
 
12
12
  ```
13
- # Kênh pre-release (hiện tại — v0.1.0-rc.2)
13
+ # Kênh pre-release (hiện tại — v0.1.0-rc.3, ships skill v0.22.0)
14
14
  npx @manhquy/flow-skill@rc
15
15
  ```
16
16
 
17
17
  Prompt tương tác hỏi cài vào agent nào. Chọn một hoặc nhiều, xác nhận, xong.
18
18
 
19
- > **Giai đoạn RC**: pin `@rc` (dist-tag) hoặc phiên bản cụ thể `@0.1.0-rc.2`. `npx @manhquy/flow-skill@0.1.x` chỉ dùng được sau khi stable `0.1.0` publish — semver range **không** match pre-release theo mặc định. Xem [SECURITY.md](./SECURITY.md).
19
+ > **Giai đoạn RC**: pin `@rc` (dist-tag) hoặc phiên bản cụ thể `@0.1.0-rc.3`. `npx @manhquy/flow-skill@0.1.x` chỉ dùng được sau khi stable `0.1.0` publish — semver range **không** match pre-release theo mặc định. Xem [SECURITY.md](./SECURITY.md).
20
+ >
21
+ > **`npm i` alone chưa đủ** — phải **chạy** CLI (`npx @manhquy/flow-skill@rc`). Hai trục version: package npm vs skill product (`ships skill v…` / `skillVersion` trong JSONL).
20
22
 
21
23
  ## Non-interactive
22
24
 
@@ -110,6 +112,11 @@ Mỗi phiên bản publish có [npm provenance](https://docs.npmjs.com/generatin
110
112
  npm view @manhquy/flow-skill@<version> dist.attestations.provenance
111
113
  ```
112
114
 
115
+ > **Ngoại lệ RC-window**: `v0.1.0-rc.1` được publish thủ công từ máy developer để bootstrap npm
116
+ > Trusted Publisher (TP không thể bind tới package chưa tồn tại) — bản này không có provenance.
117
+ > Từ `v0.1.0-rc.2` trở đi, mọi bản publish đều qua CI workflow với OIDC và có attestation
118
+ > SLSA Build Level 2 đầy đủ.
119
+
113
120
  ## Bảo mật
114
121
 
115
122
  Threat model + WILL/WON'T list + kênh report — xem [SECURITY.md](./SECURITY.md). Tóm tắt: pure Node, không network call, không spawn shell/PowerShell, không postinstall hook, không chạm settings/hooks/MCP config.
package/SECURITY.md CHANGED
@@ -55,7 +55,7 @@ These match the behavior of the upstream `install.sh`; they are documented rathe
55
55
  ```
56
56
  npm view @manhquy/flow-skill@<version> dist.attestations.provenance
57
57
  ```
58
- **`v0.1.0-rc.1` exception**: this bootstrap version was published manually (Trusted Publisher requires the package to already exist before it can bind) and carries no attestation. All subsequent versions publish via CI and are attested. If provenance is a hard requirement for you, wait for `v0.1.0-rc.2` or later.
58
+ **`v0.1.0-rc.1` exception**: this bootstrap version was published manually (Trusted Publisher requires the package to already exist before it can bind) and carries no attestation. `v0.1.0-rc.2` (2026-07-17) is the first version published from CI via OIDC — confirmed live with an SLSA Build Level 2 attestation. All versions from rc.2 onward are attested; if provenance is a hard requirement, pin `@0.1.0-rc.2` or later.
59
59
  - **Publisher account.** npm 2FA `auth-and-writes` is enabled as defense-in-depth for the git + npm identity behind the workflow.
60
60
  - **Pin your version.** During the RC window use `@rc` (dist-tag) or an explicit `@0.1.0-rc.N`. After stable ships, pin `@0.1.x` (or a specific version). `@latest` invites blind updates on a package that writes to your agent home.
61
61
  - **Completeness signal (not tamper detection).** The tarball ships `skills-manifest.json` recording the synced skill's file count + list. The publish workflow re-counts the checked-out tree against this manifest to catch an incomplete sync. It is **not** a supply-chain integrity proof — that role belongs to npm provenance above. `.integrity` SHA-256 sidecar was intentionally not adopted because it would have been generated from the same source it hashed (circular trust).
package/bin/cli.mjs CHANGED
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url';
4
4
  import { join, resolve } from 'node:path';
5
5
  import { existsSync } from 'node:fs';
6
6
 
7
- import { TARGETS, TARGET_NAMES, PKG_VERSION } from '../src/constants.mjs';
7
+ import { TARGETS, TARGET_NAMES, PKG_VERSION, SKILL_VERSION } from '../src/constants.mjs';
8
8
  import { defaultSelection, detectAll } from '../src/detect.mjs';
9
9
  import { renderHelp } from '../src/help.mjs';
10
10
  import { install, installAntigravity } from '../src/installer.mjs';
@@ -288,6 +288,9 @@ async function main() {
288
288
  {
289
289
  event: 'plan',
290
290
  version: PKG_VERSION,
291
+ // Additive: skill product version (SKILL.md). `version` stays the npm package version
292
+ // for back-compat with existing JSONL consumers.
293
+ skillVersion: SKILL_VERSION,
291
294
  dryRun: opts.dryRun,
292
295
  scope,
293
296
  targets: plan.map((p) => p.target),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manhquy/flow-skill",
3
- "version": "0.1.0-rc.2",
3
+ "version": "0.1.0",
4
4
  "description": "Install the flow skill (buildflow gated build process) into your coding agent(s) via one npx command",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,7 @@ keywords: [flow, buildflow, gate, build, ship, scope, prd, contract, card, deplo
8
8
  license: MIT
9
9
  metadata:
10
10
  author: flow-skill
11
- version: "0.22.0"
11
+ version: "0.24.0"
12
12
  attribution: "Methodology from ai20k-build-phase/buildflow (Tony, arealisticdreamer.com); harness/agent layers from repository-harness, claudekit-engineer, BMAD-METHOD. v0.22 concierge routing pattern adapted from BMAD-METHOD's bmad-help; forge-idea ritual adapted from BMAD-METHOD's bmad-forge-idea (both MIT, BMad Code LLC)."
13
13
  ---
14
14
 
@@ -235,7 +235,7 @@ Hard stops (iteration/token/time caps) and ground-truth gates (`flow.sh` exit, r
235
235
  - `references/gate-eval.md` — `/flow eval` behavioral proof for the semantic layer: what it
236
236
  measures (a fresh-judge lower bound, NOT the work-mode self-challenge), cost, thresholds,
237
237
  and the honest authorship-bias limitation. Read before running or relaying a scorecard.
238
- - `harness/` — durable layer (`flow.sh harness ...`): intake/story/trace/decision/backlog. See `harness/README.md`. Read it back with `/flow recall` (open debt, retro, previous-card, friction, backlog, playbooks) — this is the capture→reuse loop.
238
+ - `harness/` — durable layer (`flow.sh harness ...`): intake/story/**complete**/trace/decision/backlog. Pins: protocol floor `harness-cli-v0.1.14`, trust CLI `0.1.17` (never `0.1.16`). See `harness/README.md` + `harness/GAP-MATRIX-0.1.17.md`. Never `story update --status implemented` — use `story complete --proof-source …`.
239
239
  - `playbooks/` — paid-for stack knowledge: read before building a card on that stack, harvest the lesson after.
240
240
  - `_templates/` — the 7 artifacts the runner copies into `flow/` and `cards/`. Never edit during a run.
241
241
 
@@ -0,0 +1,41 @@
1
+ # Gap matrix: flow durable layer vs repository-harness CLI 0.1.17
2
+
3
+ **Authority:** `repository-harness` (local `D:\project\flow\repository-harness`).
4
+ **Pins:** protocol floor **`harness-cli-v0.1.14`** · trust/consumer CLI **`harness-cli-v0.1.17`** · **never** install/use **`0.1.16`** release assets (tag without published proof).
5
+ **Scope:** inventory only — does **not** claim US-101 bit-identical or protocol-v1 parity for flow Python.
6
+
7
+ Supersedes: brainstorm 260703 Thread1 (“nothing to upgrade”).
8
+
9
+ ## Schema
10
+
11
+ | Ver | Flow | Harness 0.1.17 | Notes |
12
+ |-----|------|----------------|-------|
13
+ | 001–004 | present (shared) | present | Hash-identical at last measure (2026-07-18) |
14
+ | 005 | present | present | File hash may differ on comments only — do not overclaim DDL fork |
15
+ | 006–008 | **absent** (reserved gap) | changesets / deps / hierarchy | Not adopted this plan |
16
+ | 009–012 | **usage/accessed** (flow-only) | improvement identity / links / closure | **Semantic collision** — same numbers, different DDL |
17
+ | 013 | absent | changeset content sha | Not adopted |
18
+
19
+ ## Commands / invariants
20
+
21
+ | Surface | Flow status | Harness 0.1.17 | Trust note |
22
+ |---------|-------------|----------------|------------|
23
+ | `story update --status implemented` | rejected (trust-align) | rejected (US-101) | Use `story complete` |
24
+ | `story complete` | flow-native flags + honest `proof_source` | `story complete` + live verify | Flow may complete via `card_markdown_gate` **without** setting `last_verified_result=pass` |
25
+ | `story verify` | optional shell verify_command | required for shell-proven complete | shell=True = operator-authored only |
26
+ | `query sql` | **not exposed** | read-only connection | Do not add mutating SQL |
27
+ | Rust forward | refused on flow-lineage DB | n/a | `FLOW_HARNESS_BACKEND=rust` → exit 2 when usage mirror / schema≥9 |
28
+ | Changesets / work-graph | not ported | 006–013 | Out of scope (FOMO red line) |
29
+ | `query contract --json` | not on Python CLI | protocol v1 discovery | Optional external binary smoke only |
30
+
31
+ ## Pins (consumer)
32
+
33
+ ```
34
+ HARNESS_PROTOCOL_V1_TAG = harness-cli-v0.1.14 # floor
35
+ HARNESS_CLI_TRUST_TAG = harness-cli-v0.1.17 # trust features + release proof
36
+ DO_NOT_USE = harness-cli-v0.1.16 # no assets / failed promotion
37
+ ```
38
+
39
+ ## Rust refuse-forward
40
+
41
+ Any DB with `usage_event` or `MAX(schema_version) >= 9` is **flow-lineage**. Python entrypoint must refuse forwarding to external `harness-cli` (exit 2). Never unfreeze on `.flow/harness.db` without schema re-home ≥014 (separate epic).
@@ -2,10 +2,20 @@
2
2
 
3
3
  The durable memory of `/flow`: intake classification, story packets with proof status,
4
4
  auto-scored execution traces, decision records, and the growth-rule backlog. Ported from
5
- `repository-harness` (same SQLite schema, `schema/00N-*.sql` verbatim). State lives at
5
+ `repository-harness` (shared base schema + flow-only usage extensions). State lives at
6
6
  `<project>/.flow/harness.db` and survives across sessions — this is the "external memory"
7
7
  that fights context rot.
8
8
 
9
+ ## Authority pins (repository-harness)
10
+
11
+ | Pin | Tag | Use |
12
+ |-----|-----|-----|
13
+ | Protocol floor | **`harness-cli-v0.1.14`** | protocol v1 discovery floor |
14
+ | Trust / consumer | **`harness-cli-v0.1.17`** | US-101 spirit + release proof |
15
+ | **Do not use** | `harness-cli-v0.1.16` | tag without published assets |
16
+
17
+ Gap inventory: [`GAP-MATRIX-0.1.17.md`](./GAP-MATRIX-0.1.17.md). Flow does **not** claim bit-identical US-101.
18
+
9
19
  ## Backends
10
20
 
11
21
  | Backend | When | How |
@@ -23,12 +33,36 @@ power-path for non-flow DBs; flow does not build or ship the binary.
23
33
 
24
34
  Disable the durable layer entirely with `FLOW_HARNESS_DISABLE=1` (engine still runs).
25
35
 
36
+ ### STRICT durable writes (`flow.sh`)
37
+
38
+ | Env | Behavior |
39
+ |-----|----------|
40
+ | unset | soft: engine exit 0; one-line `flow-harness: warn` on fail |
41
+ | `FLOW_HARNESS_STRICT=1` | soft exit; louder stderr |
42
+ | `FLOW_HARNESS_STRICT=fail` | propagate nonzero from durable ops on card/check |
43
+
44
+ ## Story completion (trust boundary)
45
+
46
+ - **Rejected:** `story update --status implemented`
47
+ - **Use:** `story complete --id <id> --proof-source card_markdown_gate|manual|verify_command [--evidence …]`
48
+ - `card_markdown_gate` / `manual` set status implemented with **honest** `proof_source=…` in notes — **never** forge `last_verified_result=pass`
49
+ - `verify_command` requires prior `story verify` pass
50
+
51
+ ## Versioning (harness vs npm)
52
+
53
+ - Telemetry / usage events carry **`flow_version` from the installed skill product**
54
+ (`SKILL.md` metadata), not from the npm installer package version.
55
+ - When you change harness schema or CLI behavior, bump the **skill product** version
56
+ (SKILL.md + plugin.json + portable-manifest) and run `flow.sh coherence`.
57
+ - Publishing a new npm tarball is a separate step (`npm-wrapper` sync + tag `npm@…`) —
58
+ see [`docs/release-process.md`](../../../docs/release-process.md).
59
+
26
60
  ## Commands ↔ Runtime-Substrate responsibility
27
61
 
28
62
  | Command | Responsibility (of 11) |
29
63
  |---|---|
30
64
  | `intake --type <t> --summary <s> [--flags ...] [--lane ...] [--narrow-scope]` | Task specification + risk classification |
31
- | `story add\|update\|verify\|verify-all` | Task state + Verification (mechanical proof) |
65
+ | `story add\|update\|complete\|verify\|verify-all` | Task state + Verification (mechanical proof) |
32
66
  | `trace --summary ... [--story\|--card C-NNN] [--agent ...] [--actions ...] [--files-changed ...] --outcome ...` | Observability + Failure attribution (auto-scored tier). Flags accept `-` or `_` (`--actions_taken`, `--files_changed`, `--files_read`) and `--card` is an alias of `--story`. A bad/missing flag prints a guiding "common forms" hint (not a silent exit-2). |
33
67
  | `decision add\|verify\|outcome` | Project memory (durable ADR row + companion markdown); `outcome` closes the predicted-vs-actual loop |
34
68
  | `backlog add\|close` | Entropy auditing + harness self-improvement (growth rule) |
@@ -60,14 +94,14 @@ Linking a trace to an unverified story prints a **pre-close gate** warning.
60
94
  - `/flow next` past **stage 04 (ADR)** → reminds you to record each decision durably (`decision add`) — the ADR markdown is not a durable record.
61
95
  - `/flow card` creates a card → seeds a `story` row (tracking handle).
62
96
  - `/flow check C-NNN` (todo) → `story update --status in_progress`.
63
- - `/flow check C-NNN` (done) → `story update --status implemented` + a `trace` (its **tier verdict is shown**, so thin traces are visible).
97
+ - `/flow check C-NNN` (done) → `story complete --proof-source card_markdown_gate` + a `trace` (tier verdict shown; honest provenance, no shell-verify stamp).
64
98
  - `/flow recall` → reads the durable layer back (friction + backlog + audit health) into your working context — the capture→reuse loop.
65
99
  - `/flow retro` → surfaces `propose` (deterministic improvement proposals from repeated friction/interventions + audit drift) for you to commit.
66
100
  - `/flow harness <args>` exposes the full CLI directly.
67
101
  All wiring degrades gracefully: if python is absent or `FLOW_HARNESS_DISABLE=1`, the
68
102
  mechanical engine runs unchanged.
69
103
 
70
- ## Usage signal: accessed_count (read-only, schema 005)
104
+ ## Usage signal: accessed_count (read-only, schema 009 — see Backends re-homing note above)
71
105
  `query decisions|friction|backlog` increment an `accessed_count` on every surfaced row and order
72
106
  output **most-reused-first**, so `recall` floats the knowledge you actually reuse to the top. It is
73
107
  a **read-only ordering signal** — never used to prune, delete, or archive a row. Low count is not
@@ -76,7 +110,7 @@ rare*. Security-class rows (`auth|authoriz|admin|tenan|payment|migrat|valid|secr
76
110
  always sort first and are never deprioritized. The story `matrix` view is intentionally left
77
111
  ordered by id (it is a status table, not recalled knowledge).
78
112
 
79
- ## Usage log: mechanical flight-recorder (schema 006)
113
+ ## Usage log: mechanical flight-recorder (schema 010 — see Backends re-homing note above)
80
114
  Distinct from the curated, agent-authored records above: `flow.sh` itself self-records **every
81
115
  invocation** (no agent action needed) to append-only JSONL — `.flow/events.jsonl` (full, per-project)
82
116
  plus `~/.claude/flow/usage.jsonl` (compact, device-global). Fields: ts/epoch_s, session_id, cycle_id
@@ -93,7 +127,7 @@ stage_from→to, card, project_type, mode, flow_version, tier, host, read_only.
93
127
  - JSONL is the source of truth; `usage_event` is a derived, queryable mirror. Semantic events keep
94
128
  using `trace`/`intervention`/`decision` — the usage log does not duplicate them.
95
129
 
96
- **Closed feedback loop (schema 007).** The recorded data feeds the surfaces where you already act:
130
+ **Closed feedback loop (schema 011 — see Backends re-homing note above).** The recorded data feeds the surfaces where you already act:
97
131
  - `recall` appends a one-line digest (`flow_harness.py usage --summary`): cycles, cycle-time, gate
98
132
  fail-rate, top gate-fail stage — so build history reaches you at stage/card start (silent if no data).
99
133
  - `propose` (`_build_proposals`) emits a backlog proposal when a stage's gate fail-rate ≥ 50% across
@@ -160,6 +160,21 @@ def cmd_intake(con, a):
160
160
  return 0
161
161
 
162
162
 
163
+ _PROOF_SOURCES = ("card_markdown_gate", "manual", "verify_command")
164
+ _SECRET_RE = re.compile(
165
+ r"(?i)(token|secret|passwd|password|credential|api[_-]?key|bearer|authorization|-----begin)"
166
+ )
167
+
168
+
169
+ def _mask_evidence(text):
170
+ """Redact secret-shaped free text before durable store (trust-align RT)."""
171
+ if text is None:
172
+ return None
173
+ if _SECRET_RE.search(text):
174
+ return "***redacted***"
175
+ return text
176
+
177
+
163
178
  def cmd_story(con, a):
164
179
  if a.story_cmd == "add":
165
180
  if a.lane not in D.LANES:
@@ -169,11 +184,58 @@ def cmd_story(con, a):
169
184
  print(f"PASS: story {a.id} added (lane={a.lane})")
170
185
  return 0
171
186
  if a.story_cmd == "update":
187
+ # Trust boundary (US-101 spirit, flow-native): bare implemented is forbidden.
188
+ if a.status == "implemented":
189
+ print(
190
+ "FAIL: story update --status implemented is rejected.\n"
191
+ " Use: story complete --id <id> --proof-source card_markdown_gate|manual|verify_command\n"
192
+ " (honest proof provenance; never forge last_verified_result without story verify)"
193
+ )
194
+ return 1
172
195
  n = _db.update(con, "story", "id", a.id, status=a.status,
173
196
  unit_proof=a.unit, integration_proof=a.integration,
174
- e2e_proof=a.e2e, platform_proof=a.platform, evidence=a.evidence)
197
+ e2e_proof=a.e2e, platform_proof=a.platform,
198
+ evidence=_mask_evidence(a.evidence))
175
199
  print(f"{'PASS' if n else 'FAIL'}: story {a.id} {'updated' if n else 'not found'}")
176
200
  return 0 if n else 1
201
+ if a.story_cmd == "complete":
202
+ s = _db.one(con, "SELECT * FROM story WHERE id=?", (a.id,))
203
+ if not s:
204
+ print(f"FAIL: story {a.id} not found"); return 1
205
+ src = (a.proof_source or "").strip()
206
+ if src not in _PROOF_SOURCES:
207
+ print(
208
+ "FAIL: --proof-source required: card_markdown_gate|manual|verify_command\n"
209
+ " card_markdown_gate = /flow check markdown gate (does NOT set last_verified=pass)\n"
210
+ " verify_command = requires prior story verify pass"
211
+ )
212
+ return 1
213
+ if src == "verify_command":
214
+ if s.get("last_verified_result") != "pass":
215
+ print(
216
+ f"FAIL: proof-source verify_command requires last_verified_result=pass "
217
+ f"(got {s.get('last_verified_result')!r}). Run: story verify --id {a.id}"
218
+ )
219
+ return 1
220
+ # Honest provenance: card/manual never forge last_verified_result=pass
221
+ ev = _mask_evidence(a.evidence) or s.get("evidence") or ""
222
+ note = (s.get("notes") or "").strip()
223
+ prov = f"proof_source={src}"
224
+ if prov not in note:
225
+ note = (note + "\n" + prov).strip() if note else prov
226
+ if src == "card_markdown_gate" and "last_verified_result=pass" not in (note or ""):
227
+ # explicit honesty marker for auditors
228
+ note = note + "\nverify_stamp=not_shell"
229
+ fields = dict(status="implemented", notes=note)
230
+ if ev:
231
+ fields["evidence"] = ev
232
+ # only keep last_verified_* when real verify already passed
233
+ n = _db.update(con, "story", "id", a.id, **fields)
234
+ print(
235
+ f"PASS: story {a.id} complete (status=implemented, proof_source={src})"
236
+ + ("" if src == "verify_command" else " [no shell-verify stamp]")
237
+ )
238
+ return 0 if n else 1
177
239
  if a.story_cmd in ("verify", "verify-all"):
178
240
  if a.story_cmd == "verify":
179
241
  stories = [_db.one(con, "SELECT * FROM story WHERE id=?", (a.id,))]
@@ -983,6 +1045,13 @@ def build_parser():
983
1045
  a2.add_argument("--evidence")
984
1046
  a3 = pss.add_parser("verify"); a3.add_argument("--id", required=True)
985
1047
  pss.add_parser("verify-all")
1048
+ # flow-native complete (trust-align D7): not protocol-v1 positional parity
1049
+ ac = pss.add_parser("complete", help="mark implemented with honest proof_source (not bare update)")
1050
+ ac.add_argument("--id", required=True)
1051
+ ac.add_argument("--proof-source", dest="proof_source",
1052
+ choices=("card_markdown_gate", "manual", "verify_command"),
1053
+ help="card_markdown_gate never forges last_verified=pass")
1054
+ ac.add_argument("--evidence")
986
1055
 
987
1056
  pt = sub.add_parser("trace", help="record an agent task execution trace (auto-scored)")
988
1057
  pt.add_argument("--summary", required=True); pt.add_argument("--intake", type=int); pt.add_argument("--story", "--card")
@@ -1034,7 +1103,7 @@ def build_parser():
1034
1103
  q1 = pqs.add_parser("matrix"); q1.add_argument("--json", action="store_true"); q1.add_argument("--numeric", action="store_true")
1035
1104
  q2 = pqs.add_parser("backlog"); q2.add_argument("--open", action="store_true"); q2.add_argument("--closed", action="store_true"); q2.add_argument("--json", action="store_true")
1036
1105
  q3 = pqs.add_parser("friction"); q3.add_argument("--json", action="store_true")
1037
- q4 = pqs.add_parser("tools"); q4.add_argument("--responsibility"); q4.add_argument("--json", action="store_true"); q4.add_argument("--summary", action="store_true")
1106
+ q4 = pqs.add_parser("tools"); q4.add_argument("--responsibility"); q4.add_argument("--json", action="store_true")
1038
1107
  q4.add_argument("--capability"); q4.add_argument("--status", choices=D.TOOL_STATUSES)
1039
1108
  q5 = pqs.add_parser("decisions"); q5.add_argument("--json", action="store_true")
1040
1109
 
@@ -0,0 +1,9 @@
1
+ # Digests from GitHub Release harness-cli-v0.1.17 (fetched for pin policy)
2
+ # TAG harness-cli-v0.1.17
3
+ # PROTOCOL_FLOOR harness-cli-v0.1.14
4
+ # DO_NOT_USE harness-cli-v0.1.16
5
+ 5d13f0a024d9d69701534a879581c9433bc0069e04d052e5aacd93b91b3b7a43 harness-cli-linux-x64
6
+ 128c42c251fb31b58582342c09c68be6ed3329340fb1d8f179c88590a4539c97 harness-cli-linux-arm64
7
+ 902968f1383d45eb60e202eb98f8ce6c143cff73cb6f2ccc159a743ca5a70819 harness-cli-macos-x64
8
+ 71217dbe03d111775c5016ab751f8bedbff66e7fd6a4d90f5c8d7e0e4a205b47 harness-cli-macos-arm64
9
+ 2d7edff22ae25308c2ba23ab85bb9acb18d87eea28a9ca4efb2014a0f11a51a6 harness-cli-windows-x64.exe
@@ -54,7 +54,7 @@ gate): **`ck-predict` at ADR** (5-persona pre-decision debate) and **`ck-scenari
54
54
  | UI card | `ui-ux-designer` | — | inline + `law/DESIGN.md` | review vs DESIGN.md |
55
55
  | Repair / diagnostic | `debugger` | — | inline root-cause + fresh same-ladder subagent | `harness intervention add` |
56
56
  | Review | `code-reviewer` (+ `typescript-reviewer` or `python-reviewer` layered — see language-specialist lens) | `bmad-code-review` (3-layer adversarial) | inline | `harness intervention add` on red |
57
- | Deploy / git ship | `git-manager` | — | inline commit + PR guide | `harness story update --status implemented` |
57
+ | Deploy / git ship | `git-manager` | — | inline commit + PR guide | `harness story complete --id … --proof-source manual` (never bare `update --status implemented`) |
58
58
  | Docs sync | `docs-manager` | — | inline doc update | — |
59
59
  | Verify-live | `tester` / `web-testing` | `bmad-qa-generate-e2e-tests` | curl/Playwright | `harness story update --e2e 1` + `trace` |
60
60
 
@@ -34,7 +34,7 @@ for each todo card in card-number order:
34
34
  4. flow.sh check C-NNN must PASS (mechanical) + gate-rules semantic check
35
35
  5. merge to main in card order; deploy; VERIFY ON LIVE URL (merge != shipped)
36
36
  6. paste world-state evidence into the card; status: done
37
- 7. flow.sh harness story update --status implemented + trace; log AUTO-LOG.md
37
+ 7. flow.sh harness story complete --id … --proof-source manual (or card_markdown_gate) + trace; log AUTO-LOG.md
38
38
  8. remove worktree
39
39
  ```
40
40
 
@@ -180,15 +180,71 @@ _python() {
180
180
  return 1
181
181
  }
182
182
 
183
- harness_call() {
184
- # best-effort durable-layer write; NEVER breaks the engine if python/harness absent.
183
+ # Durable write honesty (plan trust-align D5/D8):
184
+ # unset FLOW_HARNESS_STRICT → soft: engine stays 0; one-line stderr warn on fail
185
+ # FLOW_HARNESS_STRICT=1 → soft exit; louder multi-line stderr
186
+ # FLOW_HARNESS_STRICT=fail → propagate nonzero from harness (card/check paths)
187
+ # harness_call_checked always returns the real harness exit (for callers that care).
188
+ _harness_run() {
189
+ # runs harness; prints stderr (masked lightly); echoes nothing; returns real rc
190
+ # usage: _harness_run <args...> sets _HARNESS_LAST_RC
191
+ _HARNESS_LAST_RC=0
185
192
  [ -n "${FLOW_HARNESS_DISABLE:-}" ] && return 0
186
193
  [ -f "$HARNESS_PY" ] || return 0
187
194
  local py; py="$(_python)"; [ -n "$py" ] || return 0
188
- FLOW_PROJECT_ROOT="$ROOT" "$py" "$HARNESS_PY" "$@" >/dev/null 2>&1
195
+ local errf out rc=0
196
+ errf="$(mktemp 2>/dev/null || echo "$ROOT/.flow/.harness-err.$$")"
197
+ mkdir -p "$ROOT/.flow" 2>/dev/null || true
198
+ # capture rc without flipping the caller's set -e state
199
+ out="$(FLOW_PROJECT_ROOT="$ROOT" "$py" "$HARNESS_PY" "$@" 2>"$errf")" || rc=$?
200
+ _HARNESS_LAST_RC=$rc
201
+ if [ "$rc" -ne 0 ]; then
202
+ local errline summary
203
+ errline="$(tr -d '\r' <"$errf" 2>/dev/null | head -n 3 | tr '\n' ' ')"
204
+ # light redact of secret-shaped stderr / never dump free-text evidence args
205
+ case "$errline" in
206
+ *token*|*secret*|*passwd*|*password*|*credential*|*api_key*|*bearer*) errline="***redacted***" ;;
207
+ esac
208
+ summary="$1"
209
+ [ -n "${2:-}" ] && summary="$summary $2"
210
+ [ -n "${3:-}" ] && summary="$summary $3"
211
+ local mode="${FLOW_HARNESS_STRICT:-}"
212
+ if [ "$mode" = "1" ] || [ "$mode" = "fail" ]; then
213
+ printf 'flow-harness: durable command failed (rc=%s) cmd=%s\nflow-harness: %s\n' \
214
+ "$rc" "$summary" "$errline" >&2
215
+ else
216
+ printf 'flow-harness: warn durable write failed (rc=%s)\n' "$rc" >&2
217
+ fi
218
+ fi
219
+ rm -f "$errf" 2>/dev/null || true
220
+ return "$rc"
221
+ }
222
+
223
+ harness_call() {
224
+ # soft durable write: NEVER breaks engine unless STRICT=fail
225
+ _harness_run "$@" || true
226
+ if [ "${FLOW_HARNESS_STRICT:-}" = "fail" ] && [ "${_HARNESS_LAST_RC:-0}" -ne 0 ]; then
227
+ return "${_HARNESS_LAST_RC}"
228
+ fi
189
229
  return 0
190
230
  }
191
231
 
232
+ harness_call_checked() {
233
+ # returns real harness exit code (and still prints STRICT-aware warnings)
234
+ _harness_run "$@"
235
+ }
236
+
237
+ # When STRICT=fail, surface durable failure from card/check call sites (D5).
238
+ _harness_or_fail() {
239
+ harness_call "$@" || {
240
+ if [ "${FLOW_HARNESS_STRICT:-}" = "fail" ]; then
241
+ echo "FAIL: durable harness write failed (FLOW_HARNESS_STRICT=fail)."
242
+ return 1
243
+ fi
244
+ return 0
245
+ }
246
+ }
247
+
192
248
  harness_available() { # 0 = durable layer usable (python + harness present, not disabled)
193
249
  [ -n "${FLOW_HARNESS_DISABLE:-}" ] && return 1
194
250
  [ -f "$HARNESS_PY" ] || return 1
@@ -973,7 +1029,7 @@ cmd_card_start() { # mark a card actively in progress (operator-visible in_progr
973
1029
  local id; id="$(basename "$file" .md)"; FLOW_LOG_CARD="$id"
974
1030
  if [ "$(card_status "$file")" = "done" ]; then echo "FAIL: $id is already done — nothing to start."; return 1; fi
975
1031
  _inflight_set "$id"
976
- harness_call story update --id "$id" --status in_progress # durable mirror (best-effort)
1032
+ _harness_or_fail story update --id "$id" --status in_progress # durable mirror (STRICT=fail propagates)
977
1033
  echo "PASS: $id marked in flight. Build it, then '/flow card done $id' (or hand-edit status: done + '/flow check $id')."
978
1034
  return 0
979
1035
  }
@@ -1033,7 +1089,7 @@ cmd_card() {
1033
1089
  [ -n "$pc" ] && printf '%s\n' "$pc" | sed 's/^/ /'
1034
1090
  [ -n "$dbt" ] && { echo " open debt:"; printf '%s\n' "$dbt" | sed 's/^/ /'; }
1035
1091
  fi
1036
- harness_call story add --id "$id" --title "$id" --lane normal # durable tracking handle
1092
+ _harness_or_fail story add --id "$id" --title "$id" --lane normal # durable tracking handle
1037
1093
  return 0
1038
1094
  }
1039
1095
 
@@ -1106,9 +1162,22 @@ cmd_check() {
1106
1162
  if [ "$found" -eq 0 ]; then
1107
1163
  echo "PASS: $id is valid (status: $st)."
1108
1164
  case "$st" in
1109
- todo) harness_call story update --id "$id" --status in_progress ;;
1110
- done) harness_call story update --id "$id" --status implemented
1111
- local tr; tr="$(harness_emit trace --summary "card $id reached done-evidence" --story "$id" --outcome completed)"
1165
+ todo) _harness_or_fail story update --id "$id" --status in_progress || return 1 ;;
1166
+ done)
1167
+ # trust-align: complete with honest card_markdown_gate provenance (D3/D7/D8)
1168
+ if ! harness_call_checked story complete --id "$id" \
1169
+ --proof-source card_markdown_gate \
1170
+ --evidence "card $id markdown gate PASS"; then
1171
+ if [ "${FLOW_HARNESS_STRICT:-}" = "fail" ]; then
1172
+ echo "FAIL: durable story complete failed (FLOW_HARNESS_STRICT=fail)."
1173
+ return 1
1174
+ fi
1175
+ fi
1176
+ # auto-trace: enrich tier-2 fields from known card context (do not fake --lane tiny)
1177
+ local tr
1178
+ tr="$(harness_emit trace --summary "card $id reached done-evidence" --story "$id" \
1179
+ --outcome completed --agent flow-check \
1180
+ --actions "check $id gate PASS" --files-changed "cards/$id.md" --friction none)"
1112
1181
  [ -n "$tr" ] && printf '%s\n' "$tr" | grep -iE 'tier|to reach' | sed 's/^/ harness: /' ;;
1113
1182
  esac
1114
1183
  return 0
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "source": "../skills/flow",
3
3
  "destRelative": "skills/flow",
4
- "fileCount": 74,
5
- "syncedAt": "2026-07-17T04:53:07.141Z",
4
+ "fileCount": 76,
5
+ "syncedAt": "2026-07-19T14:46:19.032Z",
6
6
  "fileList": [
7
7
  "SKILL.md",
8
8
  "_templates/00-idea.md",
@@ -31,11 +31,13 @@
31
31
  "eval/fixtures/routing/states/state-review-pending.txt",
32
32
  "eval/fixtures/routing/states/state-scope-stage.txt",
33
33
  "eval/manifest.tsv",
34
+ "harness/GAP-MATRIX-0.1.17.md",
34
35
  "harness/README.md",
35
36
  "harness/_db.py",
36
37
  "harness/_domain.py",
37
38
  "harness/_presence.py",
38
39
  "harness/flow_harness.py",
40
+ "harness/pins/harness-cli-v0.1.17.sha256sums",
39
41
  "harness/repo_map.py",
40
42
  "harness/schema/001-init.sql",
41
43
  "harness/schema/002-story-verify.sql",
package/src/constants.mjs CHANGED
@@ -72,11 +72,26 @@ export const CLI_NAME = 'flow-skill';
72
72
 
73
73
  // F3 fix: read version from package.json at runtime so `npm version` bumps propagate to the
74
74
  // JSON `plan` event without a hand-edit here.
75
- import { readFileSync } from 'node:fs';
75
+ import { readFileSync, existsSync } from 'node:fs';
76
76
  import { fileURLToPath } from 'node:url';
77
77
  import { dirname, resolve } from 'node:path';
78
78
  const __dirname = dirname(fileURLToPath(import.meta.url));
79
+ const pkgRoot = resolve(__dirname, '..');
79
80
  const pkgJson = JSON.parse(
80
- readFileSync(resolve(__dirname, '..', 'package.json'), 'utf8')
81
+ readFileSync(resolve(pkgRoot, 'package.json'), 'utf8')
81
82
  );
82
83
  export const PKG_VERSION = pkgJson.version;
84
+
85
+ // Skill product version (SKILL.md metadata.version) is a SEPARATE axis from the npm
86
+ // installer package version. Surface both so `npx … --help` / plan events cannot be
87
+ // misread as "stale skill" when the user expected 0.22.x and saw 0.1.0-rc.N.
88
+ function readSkillVersion() {
89
+ const skillMd = resolve(pkgRoot, 'skills', 'flow', 'SKILL.md');
90
+ if (!existsSync(skillMd)) return null;
91
+ const text = readFileSync(skillMd, 'utf8');
92
+ // Frontmatter only — first `version: "x.y.z"` under metadata (or any YAML version line).
93
+ // Indented under `metadata:` in YAML frontmatter (two spaces typical).
94
+ const m = text.match(/^\s*version:\s*["']?([0-9][0-9A-Za-z.+-]*)["']?\s*$/m);
95
+ return m ? m[1] : null;
96
+ }
97
+ export const SKILL_VERSION = readSkillVersion();
package/src/help.mjs CHANGED
@@ -1,16 +1,18 @@
1
- import { CLI_NAME, PKG_VERSION, TARGETS } from './constants.mjs';
1
+ import { CLI_NAME, PKG_VERSION, SKILL_VERSION, TARGETS } from './constants.mjs';
2
2
 
3
3
  export function renderHelp() {
4
4
  const targetRows = TARGETS.map((t) => ` ${t.name.padEnd(12)} ${t.label}`).join('\n');
5
5
  // v0.23 H5 (red-team): derive the count from TARGETS.length so a future target addition can
6
6
  // never again leave a stale hardcoded number in the help text.
7
7
  const targetCount = TARGETS.length;
8
- return `${CLI_NAME} v${PKG_VERSION}
8
+ const skillBit = SKILL_VERSION ? ` (ships skill v${SKILL_VERSION})` : '';
9
+ return `${CLI_NAME} v${PKG_VERSION}${skillBit}
9
10
 
10
11
  Install the flow skill into your coding agent(s).
11
12
 
12
13
  USAGE
13
- npx @manhquy/flow-skill [options]
14
+ npx @manhquy/flow-skill@rc
15
+ # or after stable ships: npx @manhquy/flow-skill
14
16
 
15
17
  OPTIONS
16
18
  -y, --yes Skip prompts; install to default selection (detected + Claude)