runward 0.13.0 → 0.13.2
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 +3 -1
- package/dist/commands/init.js +5 -1
- package/dist/commands/status.js +54 -2
- package/dist/lib/tools.js +72 -2
- package/package.json +1 -1
- package/templates/targets/AGENTS.md +1 -0
package/README.md
CHANGED
|
@@ -137,7 +137,9 @@ Read the doctrine: [Concevoir et exécuter des systèmes agentiques (FR)](https:
|
|
|
137
137
|
|
|
138
138
|
## Supported tools
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
Two vendor-neutral files are **always written**, so the method works on any agent with no profile at all: `AGENTS.md` (the standard charter read by Codex, Cursor, Copilot, Windsurf, Cline, Zed, Amp, opencode, Junie, Warp and more), and the **phase skills** at `.agents/skills/runward-<phase>/SKILL.md` — the converged `SKILL.md` seam read by 14+ harnesses, which surface each build phase's craft rules *by relevance* at the point of action (subordinate to the gate: a skill loaded but not applied still fails `check --strict`).
|
|
141
|
+
|
|
142
|
+
Tool profiles (`--tools`) add harness-specific wiring on top: **Claude Code** (`/rw-*` commands + `.claude/skills/`), **Cursor**, **GitHub Copilot**, **Gemini CLI**, **Windsurf** (charter files), **Continue.dev** (`.continue/rules/`), **JetBrains Junie** and **Trae** (`.<harness>/skills/`). The mission structure itself is plain markdown in your repo. More profiles welcome (see [ROADMAP.md](ROADMAP.md)).
|
|
141
143
|
|
|
142
144
|
## License
|
|
143
145
|
|
package/dist/commands/init.js
CHANGED
|
@@ -2,7 +2,7 @@ import { join, resolve } from "node:path";
|
|
|
2
2
|
import { readdirSync } from "node:fs";
|
|
3
3
|
import { checkbox, input, select } from "@inquirer/prompts";
|
|
4
4
|
import { TEMPLATES, EXAMPLE_MISSION, MISSION_LAYOUT, VERSION } from "../lib/paths.js";
|
|
5
|
-
import { TOOL_PROFILES, TOOL_IDS } from "../lib/tools.js";
|
|
5
|
+
import { TOOL_PROFILES, TOOL_IDS, baselineSkills } from "../lib/tools.js";
|
|
6
6
|
import { makeWriter } from "../lib/write.js";
|
|
7
7
|
import { c, createHeader, isNonInteractive, section, status } from "../lib/styles.js";
|
|
8
8
|
/** Recursively copy a shipped directory tree (used to lay down the filled reference mission). */
|
|
@@ -100,6 +100,10 @@ export async function initCommand(opts) {
|
|
|
100
100
|
}
|
|
101
101
|
console.log(section("Agent charter"));
|
|
102
102
|
w.copy(join(TEMPLATES, "targets", "AGENTS.md"), join(root, "AGENTS.md"));
|
|
103
|
+
// Vendor-neutral phase skills (ADR-0018): the converged SKILL.md alias read by 14+ harnesses.
|
|
104
|
+
console.log(section("Phase skills (.agents/skills — vendor-neutral, relevance-loaded)"));
|
|
105
|
+
for (const f of baselineSkills(root))
|
|
106
|
+
w.write(f.path, f.content);
|
|
103
107
|
for (const profile of TOOL_PROFILES.filter((p) => tools.includes(p.id))) {
|
|
104
108
|
console.log(section(`Profile · ${profile.label}`));
|
|
105
109
|
for (const f of profile.files(root))
|
package/dist/commands/status.js
CHANGED
|
@@ -3,7 +3,8 @@ import { join, resolve } from "node:path";
|
|
|
3
3
|
import { analyze, findMissionRoot } from "../lib/mission.js";
|
|
4
4
|
import { c, createHeader, section } from "../lib/styles.js";
|
|
5
5
|
import { VERSION, WORKFLOWS } from "../lib/paths.js";
|
|
6
|
-
/** Mission snapshot: phase,
|
|
6
|
+
/** Mission snapshot: phase progress, decision journal, activity, workflows —
|
|
7
|
+
* a transmission-ready "where the mission stands", read-only from the mission files. */
|
|
7
8
|
export async function statusCommand(opts) {
|
|
8
9
|
const root = findMissionRoot(resolve(process.cwd(), opts.path ?? "."));
|
|
9
10
|
if (!root) {
|
|
@@ -13,7 +14,7 @@ export async function statusCommand(opts) {
|
|
|
13
14
|
const mission = join(root, "runward");
|
|
14
15
|
const report = analyze(mission);
|
|
15
16
|
console.log(createHeader(`Runward v${VERSION} — mission status`, root));
|
|
16
|
-
//
|
|
17
|
+
// Mission title + current gate
|
|
17
18
|
console.log(section("Mission"));
|
|
18
19
|
const framingPath = join(mission, "framing.md");
|
|
19
20
|
if (existsSync(framingPath)) {
|
|
@@ -21,6 +22,25 @@ export async function statusCommand(opts) {
|
|
|
21
22
|
console.log(` ${c.white(title)}`);
|
|
22
23
|
}
|
|
23
24
|
console.log(` ${c.primaryBold("Current gate")} ${c.white(report.currentPhase)}`);
|
|
25
|
+
// Phase progress across the gated arc — where the mission stands, gate by gate
|
|
26
|
+
console.log(section("Phase progress"));
|
|
27
|
+
const currentIndex = report.phases.findIndex((p) => !p.complete);
|
|
28
|
+
report.phases.forEach((p, i) => {
|
|
29
|
+
const filled = p.artifacts.filter((a) => a.state === "filled").length;
|
|
30
|
+
const total = p.artifacts.length;
|
|
31
|
+
const isCurrent = i === currentIndex;
|
|
32
|
+
const mark = p.complete ? c.success("✓") : isCurrent ? c.primary("▸") : c.darkGray("○");
|
|
33
|
+
const label = p.complete ? c.white(p.spec.label) : isCurrent ? c.primaryBold(p.spec.label) : c.gray(p.spec.label);
|
|
34
|
+
const count = p.complete ? c.darkGray(`${filled}/${total}`) : c.white(`${filled}/${total}`);
|
|
35
|
+
console.log(` ${mark} ${label} ${count}${isCurrent ? c.primary(" ← you are here") : ""}`);
|
|
36
|
+
// Under the current phase, name exactly what is still open.
|
|
37
|
+
if (isCurrent) {
|
|
38
|
+
for (const a of p.artifacts.filter((a) => a.state !== "filled")) {
|
|
39
|
+
console.log(` ${c.darkGray("○")} ${c.gray(a.artifact.label)} ${c.darkGray(`(${a.state})`)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
// Decision journal — the ADRs, dated
|
|
24
44
|
console.log(section("Decision journal"));
|
|
25
45
|
const adrDir = join(mission, "adr");
|
|
26
46
|
const adrs = existsSync(adrDir)
|
|
@@ -38,12 +58,44 @@ export async function statusCommand(opts) {
|
|
|
38
58
|
}
|
|
39
59
|
if (adrs.length > 5)
|
|
40
60
|
console.log(c.darkGray(` … and ${adrs.length - 5} more`));
|
|
61
|
+
console.log(c.darkGray(` ${adrs.length} decision(s) traced`));
|
|
62
|
+
}
|
|
63
|
+
// Activity — the most recently touched deliverable, so a receiving team sees the last movement
|
|
64
|
+
console.log(section("Activity"));
|
|
65
|
+
let latest = null;
|
|
66
|
+
for (const p of report.phases) {
|
|
67
|
+
for (const a of p.artifacts) {
|
|
68
|
+
const abs = join(mission, a.artifact.relPath);
|
|
69
|
+
if (!existsSync(abs))
|
|
70
|
+
continue;
|
|
71
|
+
const st = statSync(abs);
|
|
72
|
+
if (!st.isFile())
|
|
73
|
+
continue;
|
|
74
|
+
const date = st.mtime.toISOString().slice(0, 10);
|
|
75
|
+
if (!latest || date > latest.date)
|
|
76
|
+
latest = { date, label: a.artifact.label };
|
|
77
|
+
}
|
|
41
78
|
}
|
|
79
|
+
if (latest)
|
|
80
|
+
console.log(` ${c.primaryBold("Last touched")} ${c.white(latest.date)} ${c.darkGray(latest.label)}`);
|
|
81
|
+
else
|
|
82
|
+
console.log(c.darkGray(" no deliverable written yet"));
|
|
83
|
+
// Workflows
|
|
42
84
|
console.log(section("Workflows"));
|
|
43
85
|
const missing = WORKFLOWS.filter((wf) => !existsSync(join(mission, "workflows", `${wf}.md`)));
|
|
44
86
|
if (missing.length === 0)
|
|
45
87
|
console.log(c.success(" ✓ ") + c.white(`all ${WORKFLOWS.length} workflows present`));
|
|
46
88
|
else
|
|
47
89
|
console.log(c.warning(" ! ") + c.white(`missing: ${missing.join(", ")} — run \`runward update\``));
|
|
90
|
+
// Next — the transmission surface: name the next gesture, never leave the reader guessing
|
|
91
|
+
console.log(section("Next"));
|
|
92
|
+
if (currentIndex === -1) {
|
|
93
|
+
console.log(` All gated deliverables are filled. Run ${c.primary("runward check --strict")} to confirm on evidence, then ${c.primary("runward compliance <regime>")} for the evidence pack.`);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const cur = report.phases[currentIndex];
|
|
97
|
+
const open = cur.artifacts.filter((a) => a.state !== "filled").length;
|
|
98
|
+
console.log(` Fill the ${open} open deliverable(s) in ${c.white(cur.spec.label)}, then run ${c.primary("runward check")} to cross the gate on evidence.`);
|
|
99
|
+
}
|
|
48
100
|
console.log();
|
|
49
101
|
}
|
package/dist/lib/tools.js
CHANGED
|
@@ -17,11 +17,61 @@ const rulesBody = [
|
|
|
17
17
|
"with a dated re-evaluation trigger. Complexity is added only on an objective trigger — no trigger, no change.",
|
|
18
18
|
"",
|
|
19
19
|
].join("\n");
|
|
20
|
+
const PHASE_SKILLS = [
|
|
21
|
+
{
|
|
22
|
+
phase: "architect", label: "architecture",
|
|
23
|
+
when: "posing the architecture of an agentic system: domain ports, the model boundary, the integration protocol, or any structural design decision taken before the stack",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
phase: "topology", label: "execution topology",
|
|
27
|
+
when: "deciding where each port's adapter runs and under which data sovereignty: placement family, the secrets/network boundary, or third-party trace export",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
phase: "floor", label: "the executable floor",
|
|
31
|
+
when: "building the smallest executable floor: the single orchestrator, the model port, deterministic guards, persistence, or baseline observability",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
phase: "govern", label: "governance",
|
|
35
|
+
when: "governing an agentic system: the threat model, prompt-injection defense, evaluation, observability, resilience, or any sensitive-action approval",
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
/** The shared body — identical guidance across harnesses; only the packaging frontmatter differs. */
|
|
39
|
+
const skillBody = (s) => [
|
|
40
|
+
`# Runward — ${s.label} craft`,
|
|
41
|
+
"",
|
|
42
|
+
`Use this when ${s.when}.`,
|
|
43
|
+
"",
|
|
44
|
+
`Confront the CRITICAL/HIGH craft rules mapped to the \`${s.phase}\` phase in \`runward/rules/\` — each rule's \`phases:\` frontmatter declares where it applies — at the point of action, not from memory. Account for each in the deliverable's \`## Rule conformance\` manifest: \`applied\` with a \`file:line\` or test, \`deviated\` with an ADR, or \`n/a\` with a reason.`,
|
|
45
|
+
"",
|
|
46
|
+
"This skill helps you *apply* the rules; it does not enforce them. `runward check --strict` is the sole authority and verifies the manifest deterministically. A rule surfaced here but not accounted for still fails the gate.",
|
|
47
|
+
"",
|
|
48
|
+
].join("\n");
|
|
49
|
+
/** SKILL.md open-format: name + description (the relevance trigger) + body. */
|
|
50
|
+
const skillMd = (s) => [
|
|
51
|
+
"---",
|
|
52
|
+
`name: runward-${s.phase}`,
|
|
53
|
+
`description: Runward ${s.label} craft rules. Use when ${s.when}.`,
|
|
54
|
+
"---",
|
|
55
|
+
"",
|
|
56
|
+
skillBody(s),
|
|
57
|
+
].join("\n");
|
|
58
|
+
/** Emit the four phase skills as SKILL.md folders under `<root>/<...dir>/runward-<phase>/SKILL.md`. */
|
|
59
|
+
const skillsAt = (root, ...dir) => PHASE_SKILLS.map((s) => ({ path: join(root, ...dir, `runward-${s.phase}`, "SKILL.md"), content: skillMd(s) }));
|
|
60
|
+
/**
|
|
61
|
+
* The vendor-neutral phase skills at `.agents/skills/` — the converged SKILL.md alias read by
|
|
62
|
+
* 14+ harnesses, no agent privileged. Always written alongside AGENTS.md, like the rules.
|
|
63
|
+
*/
|
|
64
|
+
export function baselineSkills(root) {
|
|
65
|
+
return skillsAt(root, ".agents", "skills");
|
|
66
|
+
}
|
|
20
67
|
export const TOOL_PROFILES = [
|
|
21
68
|
{
|
|
22
69
|
id: "claude",
|
|
23
|
-
label: "Claude Code (.claude/commands/rw-*)",
|
|
24
|
-
files: (root) =>
|
|
70
|
+
label: "Claude Code (.claude/commands/rw-* + .claude/skills/)",
|
|
71
|
+
files: (root) => [
|
|
72
|
+
...WORKFLOWS.map((wf) => ({ path: join(root, ".claude", "commands", `rw-${wf}.md`), content: pointer(wf) })),
|
|
73
|
+
...skillsAt(root, ".claude", "skills"),
|
|
74
|
+
],
|
|
25
75
|
},
|
|
26
76
|
{
|
|
27
77
|
id: "cursor",
|
|
@@ -55,5 +105,25 @@ export const TOOL_PROFILES = [
|
|
|
55
105
|
content: "# Runward\n\n" + rulesBody,
|
|
56
106
|
}],
|
|
57
107
|
},
|
|
108
|
+
{
|
|
109
|
+
id: "continue",
|
|
110
|
+
label: "Continue.dev (.continue/rules/runward-*)",
|
|
111
|
+
files: (root) => PHASE_SKILLS.map((s) => ({
|
|
112
|
+
path: join(root, ".continue", "rules", `runward-${s.phase}.md`),
|
|
113
|
+
content: ["---", `name: Runward ${s.label} craft`, `description: Use when ${s.when}.`, "alwaysApply: false", "---", "", skillBody(s)].join("\n"),
|
|
114
|
+
})),
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: "junie",
|
|
118
|
+
label: "JetBrains Junie (.junie/skills/)",
|
|
119
|
+
files: (root) => skillsAt(root, ".junie", "skills"),
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: "trae",
|
|
123
|
+
label: "Trae (.trae/skills/)",
|
|
124
|
+
files: (root) => skillsAt(root, ".trae", "skills"),
|
|
125
|
+
},
|
|
58
126
|
];
|
|
59
127
|
export const TOOL_IDS = TOOL_PROFILES.map((t) => t.id);
|
|
128
|
+
/** The gated build phases that get a relevance-loaded phase skill (ADR-0018). */
|
|
129
|
+
export const SKILL_PHASES = PHASE_SKILLS.map((s) => s.phase);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runward",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "After the spec: ship and run. A delivery framework for agentic systems — floor first, evolution on evidence, governance from day zero, handover with proof.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agentic",
|
|
@@ -17,6 +17,7 @@ This project is delivered with the Runward method: floor first, evolution on evi
|
|
|
17
17
|
- Before any structural decision, run `runward/workflows/decision-loop.md`: verify in the real code, check the sourced state of the art, challenge the source, take a durable position, lock it in an ADR — only then edit.
|
|
18
18
|
- One ADR per structural decision, in `runward/adr/`, with a dated re-evaluation trigger. Use the template `runward/adr/ADR-0000-template.md`.
|
|
19
19
|
- Current phase and gates: see `runward/framing.md` (Definition of Ready) and each workflow's Definition of Done. Do not skip a gate on assertion; pass it on evidence.
|
|
20
|
+
- Offer to wire the gate at your harness's lifecycle seam — git `pre-commit`, a CI required check, or a turn-end hook — from the inert samples in `runward/adapters/` (its README has the exact command). Propose it to the operator and act only on their explicit approval; never wire it silently. runward installs nothing itself (ADR-0012): you are the operator's hands, and the operator owns the gate.
|
|
20
21
|
- Show before you build: for any deliverable meant for humans, produce a reviewable preview first.
|
|
21
22
|
- Never mark a phase done if its Definition of Done is not demonstrably met.
|
|
22
23
|
|