@qa-gentic/stlc-agents 1.0.16 → 1.0.18

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.
Files changed (45) hide show
  1. package/ORCHESTRATION_RULES.md +283 -0
  2. package/README.md +246 -321
  3. package/bin/postinstall.js +26 -2
  4. package/bin/qa-stlc.js +23 -0
  5. package/package.json +15 -2
  6. package/skills/write-helix-files/SKILL.md +6 -0
  7. package/src/cli/cmd-cost.js +253 -0
  8. package/src/cli/cmd-init.js +19 -2
  9. package/src/cli/cmd-mcp-config.js +123 -62
  10. package/src/cli/cmd-skills.js +21 -4
  11. package/src/stlc_agents/agent_gherkin_generator/server.py +88 -4
  12. package/src/stlc_agents/agent_helix_writer/tools/helix_write.py +60 -28
  13. package/src/stlc_agents/agent_jira_manager/server.py +209 -2
  14. package/src/stlc_agents/agent_jira_manager/tools/jira_workitem.py +36 -0
  15. package/src/stlc_agents/agent_playwright_generator/server.py +968 -105
  16. package/src/stlc_agents/agent_test_case_manager/server.py +121 -2
  17. package/src/stlc_agents/shared/cost_tracker.py +395 -0
  18. package/src/stlc_agents/shared/install_hook.py +154 -0
  19. package/src/stlc_agents/shared/pricing.py +72 -0
  20. package/src/stlc_agents/__pycache__/__init__.cpython-310.pyc +0 -0
  21. package/src/stlc_agents/agent_gherkin_generator/__pycache__/__init__.cpython-310.pyc +0 -0
  22. package/src/stlc_agents/agent_gherkin_generator/__pycache__/server.cpython-310.pyc +0 -0
  23. package/src/stlc_agents/agent_gherkin_generator/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  24. package/src/stlc_agents/agent_gherkin_generator/tools/__pycache__/ado_gherkin.cpython-310.pyc +0 -0
  25. package/src/stlc_agents/agent_helix_writer/__pycache__/__init__.cpython-310.pyc +0 -0
  26. package/src/stlc_agents/agent_helix_writer/__pycache__/server.cpython-310.pyc +0 -0
  27. package/src/stlc_agents/agent_helix_writer/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  28. package/src/stlc_agents/agent_helix_writer/tools/__pycache__/boilerplate.cpython-310.pyc +0 -0
  29. package/src/stlc_agents/agent_helix_writer/tools/__pycache__/helix_write.cpython-310.pyc +0 -0
  30. package/src/stlc_agents/agent_jira_manager/__pycache__/__init__.cpython-310.pyc +0 -0
  31. package/src/stlc_agents/agent_jira_manager/__pycache__/server.cpython-310.pyc +0 -0
  32. package/src/stlc_agents/agent_jira_manager/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  33. package/src/stlc_agents/agent_jira_manager/tools/__pycache__/jira_workitem.cpython-310.pyc +0 -0
  34. package/src/stlc_agents/agent_playwright_generator/__pycache__/__init__.cpython-310.pyc +0 -0
  35. package/src/stlc_agents/agent_playwright_generator/__pycache__/server.cpython-310.pyc +0 -0
  36. package/src/stlc_agents/agent_playwright_generator/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  37. package/src/stlc_agents/agent_playwright_generator/tools/__pycache__/ado_attach.cpython-310.pyc +0 -0
  38. package/src/stlc_agents/agent_test_case_manager/__pycache__/__init__.cpython-310.pyc +0 -0
  39. package/src/stlc_agents/agent_test_case_manager/__pycache__/server.cpython-310.pyc +0 -0
  40. package/src/stlc_agents/agent_test_case_manager/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  41. package/src/stlc_agents/agent_test_case_manager/tools/__pycache__/ado_workitem.cpython-310.pyc +0 -0
  42. package/src/stlc_agents/shared/__pycache__/__init__.cpython-310.pyc +0 -0
  43. package/src/stlc_agents/shared/__pycache__/auth.cpython-310.pyc +0 -0
  44. package/src/stlc_agents/shared_jira/__pycache__/__init__.cpython-310.pyc +0 -0
  45. package/src/stlc_agents/shared_jira/__pycache__/auth.cpython-310.pyc +0 -0
@@ -52,7 +52,15 @@ const info = (s) => console.log(`${C.cyan}→${C.reset} ${s}`);
52
52
  const warn = (s) => console.log(`${C.yellow}⚠${C.reset} ${s}`);
53
53
  const d = (s) => `${C.dim}${s}${C.reset}`;
54
54
 
55
- console.log(`\n${b("QA STLC Agents")} v${pkg.version} — post-install\n`);
55
+ console.log(`
56
+ ${b("QA STLC Agents")} v${pkg.version} — post-install
57
+
58
+ ${d("This npm package includes:")}
59
+ • Five Python MCP servers for Azure DevOps + Jira Cloud
60
+ • Skill files for AI coding agents (Claude Code, Copilot, Cursor, Windsurf)
61
+ • ORCHESTRATION_RULES.md — reference guide for multi-step QA workflows
62
+ • Command-line tools: qa-stlc init, qa-stlc scaffold, qa-stlc skills, etc.
63
+ `);
56
64
 
57
65
  // ── 1. Find Python ────────────────────────────────────────────────────────────
58
66
  const pythonCandidates = ["python3", "python"];
@@ -84,7 +92,23 @@ if (!python) {
84
92
  }
85
93
  }
86
94
 
87
- // ── 3. Print next-step instructions ──────────────────────────────────────────
95
+ // ── 3. Activate cost tracking on MCP servers ─────────────────────────────
96
+ info("Activating cost tracking on MCP servers...");
97
+ const costPatch = spawnSync(
98
+ python,
99
+ ["-m", "stlc_agents.shared.install_hook"],
100
+ { encoding: "utf8", stdio: "pipe" }
101
+ );
102
+ if (costPatch.status === 0) {
103
+ // Print each output line through our TTY writer
104
+ (costPatch.stdout || "").split("\n").filter(Boolean).forEach((l) => console.log(l));
105
+ ok("Cost tracking active — every tool call will log tokens + cost");
106
+ } else {
107
+ warn("Cost tracking patch skipped (run manually: python -m stlc_agents.shared.install_hook)");
108
+ if (costPatch.stderr) console.log(d(costPatch.stderr.slice(0, 300)));
109
+ }
110
+
111
+ // ── 4. Print next-step instructions ──────────────────────────────────────────
88
112
  console.log(`
89
113
  ${b("Next steps")} — run these inside your project root:
90
114
 
package/bin/qa-stlc.js CHANGED
@@ -22,6 +22,7 @@ const cmdSkills = require("../src/cli/cmd-skills");
22
22
  const cmdMcpConfig = require("../src/cli/cmd-mcp-config");
23
23
  const cmdVerify = require("../src/cli/cmd-verify");
24
24
  const cmdScaffold = require("../src/cli/cmd-scaffold");
25
+ const cmdCost = require("../src/cli/cmd-cost");
25
26
 
26
27
  program
27
28
  .name("qa-stlc")
@@ -83,6 +84,28 @@ program
83
84
  .option("--no-install", "Skip npm install after scaffolding")
84
85
  .action(cmdScaffold);
85
86
 
87
+ program
88
+ .command("cost")
89
+ .description(
90
+ "Show token usage and cost for stlc-agents pipeline sessions.\n" +
91
+ "Reads logs from ~/.qa-stlc/cost-*.jsonl written on every MCP tool call.\n\n" +
92
+ "Model detection order (first match wins):\n" +
93
+ " 1. STLC_CODING_AGENT_MODEL env var (explicit override)\n" +
94
+ " 2. ANTHROPIC_MODEL env var (set automatically by Claude Code)\n" +
95
+ " 3. GITHUB_COPILOT_MODEL env var (set by Copilot if configured)\n" +
96
+ " 4. ~/.qa-stlc/agent-model file (saved by --set-model)\n" +
97
+ " 5. claude-sonnet-4-6 (default fallback)\n\n" +
98
+ "Claude Code users: no setup needed — model is detected automatically.\n" +
99
+ "Cursor / Windsurf users: run qa-stlc cost --set-model <model-id> once."
100
+ )
101
+ .option("--all", "Show all sessions (default: last session only)")
102
+ .option("--session <id>", "Show a specific session by ID")
103
+ .option("--json", "Output raw JSON instead of a formatted table")
104
+ .option("--set-model <model>","Save your coding agent model for accurate pricing.\n" +
105
+ " e.g. claude-opus-4-6 | gpt-4o | claude-sonnet-4-6")
106
+ .option("--model-help", "Explain model detection and all ways to configure it")
107
+ .action(cmdCost);
108
+
86
109
  // ── Quick-start hint appended to every --help output ─────────────────────────
87
110
  program.addHelpText("after", `
88
111
  Quick Start (run once in your project root):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qa-gentic/stlc-agents",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "QA STLC Agents — five MCP servers + skills for AI-powered test case, Gherkin, Playwright generation, and Helix-QA file writing against Azure DevOps and Jira Cloud. Full pipeline for both: fetch → test cases → Gherkin → Playwright → Helix-QA. Works with Claude Code, GitHub Copilot, Cursor, Windsurf.",
5
5
  "keywords": [
6
6
  "playwright",
@@ -37,11 +37,24 @@
37
37
  "src/",
38
38
  "skills/",
39
39
  ".github/agents/",
40
- "README.md"
40
+ "README.md",
41
+ "ORCHESTRATION_RULES.md"
41
42
  ],
42
43
  "scripts": {
43
44
  "postinstall": "node ./bin/postinstall.js"
44
45
  },
46
+ "_comment": "Diff to apply to package.json — add the cost command to qa-stlc.js",
47
+
48
+ "diff": {
49
+ "bin/qa-stlc.js": {
50
+ "add_require": "const cmdCost = require('../src/cli/cmd-cost');",
51
+ "add_command": {
52
+ "after": "// ── scaffold (or any existing last command) ──────────────────────────────",
53
+ "insert": "\n// ── cost ─────────────────────────────────────────────────────────────────\nprogram\n .command('cost')\n .description('Show token usage and cost for the current or past sessions.\\n' +\n 'Reads session logs from ~/.qa-stlc/cost-*.jsonl\\n' +\n 'Written automatically by the MCP servers on every tool call.')\n .option('--all', 'Show all sessions (default: last session only)')\n .option('--session <id>', 'Show a specific session by ID')\n .option('--json', 'Output raw JSON')\n .action(cmdCost);\n"
54
+ }
55
+ }
56
+ },
57
+ "full_command_to_add_to_qa-stlc.js": "// ── cost ─────────────────────────────────────────────────────────────────\nprogram\n .command('cost')\n .description(\n 'Show token usage and cost for the current or past pipeline sessions.\\n' +\n 'Reads logs from ~/.qa-stlc/cost-*.jsonl written by the MCP servers.\\n' +\n 'Each MCP tool call logs tokens, cost, and latency automatically.'\n )\n .option('--all', 'Show all sessions (not just the last one)')\n .option('--session <id>', 'Show a specific session by its ID')\n .option('--json', 'Emit raw JSON instead of a formatted table')\n .action(cmdCost);",
45
58
  "dependencies": {
46
59
  "commander": "^12.0.0",
47
60
  "which": "^4.0.0"
@@ -47,6 +47,12 @@ When(/^the user enters "([^"]*)" in the username field$/, async function (value:
47
47
  If `generate_playwright_code` produced regex patterns, use `pre_validate_cucumber_steps`
48
48
  to identify them, then convert to Cucumber expressions before calling `write_helix_files`.
49
49
 
50
+ **HARD STOP 4 — Cucumber config: append only, never create.**
51
+ If any Cucumber config file exists (`src/config/cucumber.config.ts` or project-root
52
+ `cucumber.js`), only append new profiles—never create a new file.
53
+ `write_helix_files` will automatically skip profile creation if the file does not
54
+ exist, to prevent orphaned config files from being left behind.
55
+
50
56
  ---
51
57
 
52
58
  Place files produced by `qa-playwright-generator` into a local Helix-QA
@@ -0,0 +1,253 @@
1
+ /**
2
+ * cmd-cost.js — `qa-stlc cost`
3
+ *
4
+ * Read session cost logs from ~/.qa-stlc/cost-*.jsonl and print a report.
5
+ * Also handles --set-model to persist the coding agent model preference.
6
+ *
7
+ * Usage:
8
+ * qa-stlc cost # last session
9
+ * qa-stlc cost --all # all sessions
10
+ * qa-stlc cost --session <id> # specific session
11
+ * qa-stlc cost --json # raw JSON output
12
+ * qa-stlc cost --set-model <model-id> # save model for Cursor/Windsurf users
13
+ *
14
+ * Model detection order (mirrors cost_tracker.py):
15
+ * STLC_CODING_AGENT_MODEL env var
16
+ * → ANTHROPIC_MODEL env var (Claude Code sets this automatically)
17
+ * → GITHUB_COPILOT_MODEL env var
18
+ * → ~/.qa-stlc/agent-model (saved by --set-model)
19
+ * → "claude-sonnet-4-6" (default)
20
+ */
21
+
22
+ "use strict";
23
+
24
+ const fs = require("fs");
25
+ const path = require("path");
26
+ const os = require("os");
27
+
28
+ const LOG_DIR = path.join(os.homedir(), ".qa-stlc");
29
+ const PREF_FILE = path.join(LOG_DIR, "agent-model");
30
+
31
+ const C = {
32
+ reset: "\x1b[0m", bold: "\x1b[1m",
33
+ dim: "\x1b[2m", cyan: "\x1b[36m",
34
+ green: "\x1b[32m", yellow: "\x1b[33m",
35
+ };
36
+ const b = (s) => `${C.bold}${s}${C.reset}`;
37
+ const dim = (s) => `${C.dim}${s}${C.reset}`;
38
+ const grn = (s) => `${C.green}${s}${C.reset}`;
39
+ const cyn = (s) => `${C.cyan}${s}${C.reset}`;
40
+
41
+ function fmtTok(n) { return n >= 1000 ? `${(n/1000).toFixed(1)}K` : String(n); }
42
+ function fmtUsd(n) { return `$${n.toFixed(6)}`; }
43
+ function fmtMs(n) { return `${n}ms`; }
44
+
45
+ // ── Model preference helpers ───────────────────────────────────────────────
46
+
47
+ const KNOWN_MODELS = {
48
+ // Anthropic
49
+ "claude-sonnet-4-6": "$3/$15 per MTok",
50
+ "claude-opus-4-6": "$5/$25 per MTok",
51
+ "claude-opus-4-7": "$5/$25 per MTok",
52
+ "claude-haiku-4-5-20251001": "$1/$5 per MTok",
53
+ "claude-sonnet-4-20250514": "$3/$15 per MTok",
54
+ // OpenAI / Copilot
55
+ "gpt-4o": "$2.50/$10 per MTok",
56
+ "gpt-4o-mini": "$0.15/$0.60 per MTok",
57
+ };
58
+
59
+ function saveModelPref(modelId) {
60
+ fs.mkdirSync(LOG_DIR, { recursive: true });
61
+ fs.writeFileSync(PREF_FILE, modelId.trim(), "utf8");
62
+ }
63
+
64
+ function readModelPref() {
65
+ try {
66
+ if (fs.existsSync(PREF_FILE)) return fs.readFileSync(PREF_FILE, "utf8").trim();
67
+ } catch (_) {}
68
+ return null;
69
+ }
70
+
71
+ function printModelHelp() {
72
+ console.log(`\n ${b("Coding agent model for cost tracking")}\n`);
73
+ console.log(` ${dim("Model is detected automatically in this order:")}`);
74
+ console.log(` 1. STLC_CODING_AGENT_MODEL env var ${dim("(explicit override, always wins)")}`);
75
+ console.log(` 2. ANTHROPIC_MODEL env var ${dim("(set automatically by Claude Code ✓ zero config)")}`);
76
+ console.log(` 3. GITHUB_COPILOT_MODEL env var ${dim("(set by Copilot if configured)")}`);
77
+ console.log(` 4. ~/.qa-stlc/agent-model file ${dim("(saved by qa-stlc cost --set-model)")}`);
78
+ console.log(` 5. claude-sonnet-4-6 ${dim("(default fallback)")}\n`);
79
+
80
+ const saved = readModelPref();
81
+ if (saved) {
82
+ console.log(` ${grn("✓")} Currently saved: ${b(saved)} ${dim("(~/.qa-stlc/agent-model)")}\n`);
83
+ }
84
+
85
+ console.log(` ${b("Known models:")}`);
86
+ for (const [id, rate] of Object.entries(KNOWN_MODELS)) {
87
+ console.log(` ${id.padEnd(36)} ${dim(rate)}`);
88
+ }
89
+
90
+ console.log(`\n ${b("To set for Cursor / Windsurf / any agent:")}`);
91
+ console.log(` ${cyn("Option A — save once (recommended):")}`);
92
+ console.log(` qa-stlc cost --set-model claude-opus-4-6\n`);
93
+ console.log(` ${cyn("Option B — shell profile (applies to all projects):")}`);
94
+ console.log(` echo 'export STLC_CODING_AGENT_MODEL=claude-opus-4-6' >> ~/.zshrc\n`);
95
+ console.log(` ${cyn("Option C — .mcp.json env block (project-level):")}`);
96
+ console.log(` "qa-gherkin-generator": {`);
97
+ console.log(` "command": "/path/to/qa-gherkin-generator",`);
98
+ console.log(` "env": { "STLC_CODING_AGENT_MODEL": "claude-opus-4-6" }`);
99
+ console.log(` }\n`);
100
+ console.log(` ${cyn("Option D — .env file in project root:")}`);
101
+ console.log(` STLC_CODING_AGENT_MODEL=claude-opus-4-6\n`);
102
+ console.log(` ${dim("Claude Code users: no action needed.")}`);
103
+ console.log(` ${dim("ANTHROPIC_MODEL is passed through automatically via qa-stlc mcp-config.")}\n`);
104
+ }
105
+
106
+ // ── Log reading ────────────────────────────────────────────────────────────
107
+
108
+ function readLogs() {
109
+ if (!fs.existsSync(LOG_DIR)) return [];
110
+ return fs.readdirSync(LOG_DIR)
111
+ .filter((f) => f.startsWith("cost-") && f.endsWith(".jsonl"))
112
+ .sort()
113
+ .map((f) => {
114
+ const file = path.join(LOG_DIR, f);
115
+ const records = fs.readFileSync(file, "utf8")
116
+ .split("\n").filter(Boolean).map((l) => JSON.parse(l));
117
+ return {
118
+ file,
119
+ sessionId: f.replace(/^cost-/, "").replace(/\.jsonl$/, ""),
120
+ records,
121
+ };
122
+ });
123
+ }
124
+
125
+ // ── Printing ───────────────────────────────────────────────────────────────
126
+
127
+ function printSession(sess) {
128
+ const { sessionId, records } = sess;
129
+ if (!records.length) return;
130
+
131
+ const byServer = {};
132
+ let totalCost = 0, totalTokens = 0;
133
+ for (const r of records) {
134
+ const k = r.server || "unknown";
135
+ if (!byServer[k]) byServer[k] = { calls: 0, tokens: 0, cost: 0 };
136
+ byServer[k].calls++;
137
+ byServer[k].tokens += r.estimated_tokens || 0;
138
+ byServer[k].cost += r.cost_usd || 0;
139
+ totalCost += r.cost_usd || 0;
140
+ totalTokens += r.estimated_tokens || 0;
141
+ }
142
+
143
+ const model = records[0]?.model || "unknown";
144
+ const source = records[0]?.model_source || "";
145
+ const method = records.some((r) => r.token_method === "estimated")
146
+ ? "estimated (payload chars÷4)" : "exact";
147
+ const ts = records[0]?.timestamp?.slice(0, 19).replace("T", " ") || "";
148
+ const tsEnd = records[records.length - 1]?.timestamp?.slice(0, 19).replace("T", " ") || "";
149
+
150
+ console.log(`\n${"═".repeat(68)}`);
151
+ console.log(b(` stlc-agents · Cost Report · ${sessionId}`));
152
+ console.log(`${"═".repeat(68)}`);
153
+ console.log(dim(` ${ts} → ${tsEnd}`));
154
+ console.log(dim(` Model: ${model} (detected via: ${source})`));
155
+ console.log(dim(` Token method: ${method}`));
156
+
157
+ // Per-server
158
+ console.log(`\n ${"Server".padEnd(30)} ${"Calls".padStart(6)} ${"~Tokens".padStart(10)} ${"Cost (USD)".padStart(14)}`);
159
+ console.log(` ${"─".repeat(60)}`);
160
+ for (const [svr, d] of Object.entries(byServer)) {
161
+ console.log(
162
+ ` ${cyn(svr.padEnd(30))} ${String(d.calls).padStart(6)} ` +
163
+ `${fmtTok(d.tokens).padStart(10)} ${grn(fmtUsd(d.cost).padStart(14))}`
164
+ );
165
+ }
166
+
167
+ // Per-step
168
+ console.log(`\n ${"Step detail"}`);
169
+ console.log(` ${"─".repeat(68)}`);
170
+ for (const r of records) {
171
+ console.log(
172
+ ` ${(r.server || "?").padEnd(26)} ${(r.tool || "?").padEnd(36)} ` +
173
+ `${fmtTok(r.estimated_tokens || 0).padStart(6)} ` +
174
+ `${grn(fmtUsd(r.cost_usd || 0))} ${fmtMs(r.latency_ms || 0)} ` +
175
+ dim(`[${r.token_method || "?"}]`)
176
+ );
177
+ }
178
+
179
+ // Totals
180
+ console.log(`\n ${"─".repeat(68)}`);
181
+ console.log(` ${"Total tokens".padEnd(40)} ${fmtTok(totalTokens).padStart(10)}`);
182
+ console.log(` ${b("Total cost".padEnd(40))} ${grn(fmtUsd(totalCost))}`);
183
+ console.log(dim(` Log: ${sess.file}`));
184
+ console.log(`${"═".repeat(68)}\n`);
185
+ }
186
+
187
+ // ── Main ───────────────────────────────────────────────────────────────────
188
+
189
+ module.exports = async function cost(opts) {
190
+
191
+ // --set-model: save model preference for Cursor/Windsurf users
192
+ if (opts.setModel) {
193
+ const modelId = opts.setModel.trim();
194
+ saveModelPref(modelId);
195
+ const rate = KNOWN_MODELS[modelId];
196
+ console.log(`\n ${grn("✓")} Model saved: ${b(modelId)}`);
197
+ if (rate) console.log(` Pricing: ${dim(rate)}`);
198
+ else console.log(` ${dim("(unknown model — pricing may show $0.000000, add to pricing.py)")}`);
199
+ console.log(` Written to: ${dim(PREF_FILE)}`);
200
+ console.log(`\n This preference is used by all stlc-agents servers in all projects.`);
201
+ console.log(` Override per-project via STLC_CODING_AGENT_MODEL in .env or .mcp.json.\n`);
202
+ return;
203
+ }
204
+
205
+ // --model-help: explain how model detection works
206
+ if (opts.modelHelp) {
207
+ printModelHelp();
208
+ return;
209
+ }
210
+
211
+ const sessions = readLogs();
212
+
213
+ if (!sessions.length) {
214
+ console.log(`\n No cost logs found in ${LOG_DIR}`);
215
+ console.log(` Run any qa-stlc MCP tool call to start tracking.\n`);
216
+ return;
217
+ }
218
+
219
+ if (opts.json) {
220
+ const all = opts.all ? sessions : [sessions[sessions.length - 1]];
221
+ console.log(JSON.stringify(all, null, 2));
222
+ return;
223
+ }
224
+
225
+ if (opts.session) {
226
+ const found = sessions.find((s) => s.sessionId === opts.session);
227
+ if (!found) {
228
+ console.log(`\n Session not found: ${opts.session}`);
229
+ console.log(` Available: ${sessions.map((s) => s.sessionId).join(", ")}\n`);
230
+ return;
231
+ }
232
+ printSession(found);
233
+ return;
234
+ }
235
+
236
+ if (opts.all) {
237
+ for (const s of sessions) printSession(s);
238
+ const allRecords = sessions.flatMap((s) => s.records);
239
+ const grandTotal = allRecords.reduce((a, r) => a + (r.cost_usd || 0), 0);
240
+ const grandTokens = allRecords.reduce((a, r) => a + (r.estimated_tokens || 0), 0);
241
+ console.log(`${"═".repeat(68)}`);
242
+ console.log(b(` All sessions — grand total`));
243
+ console.log(`${"═".repeat(68)}`);
244
+ console.log(` Sessions : ${sessions.length}`);
245
+ console.log(` Total tokens: ${fmtTok(grandTokens)}`);
246
+ console.log(b(` TOTAL COST : ${grn(fmtUsd(grandTotal))}`));
247
+ console.log(`${"═".repeat(68)}\n`);
248
+ return;
249
+ }
250
+
251
+ // Default: last session
252
+ printSession(sessions[sessions.length - 1]);
253
+ };
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * cmd-init.js — `qa-stlc init`
3
3
  *
4
- * Full bootstrap: install Python agents + skills + MCP config.
4
+ * Full bootstrap: install Python agents + skills + ORCHESTRATION_RULES.md + MCP config.
5
5
  * Accepts --integration <ado|jira|both>. When omitted, reads
6
6
  * ~/.qa-stlc/integration (written by postinstall) or prompts interactively.
7
7
  */
@@ -89,7 +89,23 @@ module.exports = async function init(opts) {
89
89
  }
90
90
  ok("qa-gentic-stlc-agents installed.");
91
91
 
92
- // ── 4. Install skills ─────────────────────────────────────────────────────
92
+ // ── 4. Copy ORCHESTRATION_RULES.md to project root ─────────────────────────
93
+ info("Installing ORCHESTRATION_RULES.md to project root…");
94
+ try {
95
+ const npmPkgDir = path.join(path.dirname(require.resolve("@qa-gentic/stlc-agents/package.json")));
96
+ const srcRules = path.join(npmPkgDir, "ORCHESTRATION_RULES.md");
97
+ const destRules = path.join(process.cwd(), "ORCHESTRATION_RULES.md");
98
+ if (fs.existsSync(srcRules)) {
99
+ fs.copyFileSync(srcRules, destRules);
100
+ ok("ORCHESTRATION_RULES.md copied to project root.");
101
+ } else {
102
+ warn("ORCHESTRATION_RULES.md not found in npm package (expected in development).");
103
+ }
104
+ } catch (e) {
105
+ // Silently skip if not available (common in dev environments before full build)
106
+ }
107
+
108
+ // ── 5. Install skills ─────────────────────────────────────────────────────
93
109
  info("Installing skills…");
94
110
  const skillTarget = opts.vscode ? "vscode" : "claude";
95
111
  await cmdSkills({ target: skillTarget, integration });
@@ -132,6 +148,7 @@ ${C.bold}Setup complete.${C.reset}
132
148
  ${C.dim}Integration:${C.reset} ${C.bold}${integration}${C.reset}
133
149
  ${C.dim}MCP config :${C.reset} ${mcpLocation}
134
150
  ${C.dim}Skills :${C.reset} ${skillsLocation}
151
+ ${C.dim}Rules :${C.reset} ORCHESTRATION_RULES.md ${C.dim}(project root — reference for multi-step workflows)${C.reset}
135
152
 
136
153
  ${C.bold}Start Playwright MCP${C.reset} ${C.dim}(keep running in a separate terminal):${C.reset}
137
154