qatools-cli 1.1.4 → 1.1.6

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 (2) hide show
  1. package/package.json +1 -1
  2. package/qatools.js +269 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qatools-cli",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "CLI for QA Tools — requirements, epics, sprints, sessions, App Context sync, project agenda. The companion to qa-tools.com.",
5
5
  "bin": {
6
6
  "qatools": "./qatools.js"
package/qatools.js CHANGED
@@ -684,6 +684,7 @@ program
684
684
  .option("--project <id>", "Target project ID (overrides default)")
685
685
  .option("-o, --output <path>", "Path to file to update (auto: .claude/CLAUDE.md if it exists, else CLAUDE.md)")
686
686
  .option("--check", "Exit 1 if file is out of sync (does not modify)")
687
+ .option("--dry-run", "Show what would change without writing the file")
687
688
  .option("-e, --email <email>", "Login email")
688
689
  .option("-p, --password <password>", "Login password")
689
690
  .action(async (options) => {
@@ -739,6 +740,49 @@ program
739
740
  }
740
741
  }
741
742
 
743
+ if (options.dryRun) {
744
+ if (existing === updated) {
745
+ console.log(`✓ ${options.output} already in sync — nothing would change.`);
746
+ process.exit(0);
747
+ }
748
+ const beginIdxNow = existing.indexOf(beginMarker);
749
+ if (beginIdxNow < 0) {
750
+ console.log(`Would CREATE qatools-context block in ${options.output}:`);
751
+ console.log(` + ${md.split('\n').length} lines (${md.length} chars)`);
752
+ console.log(` Block markers: ${beginMarker} ... ${endMarker}`);
753
+ console.log(``);
754
+ console.log(`First 10 lines of new content:`);
755
+ md.split('\n').slice(0, 10).forEach(l => console.log(` + ${l}`));
756
+ process.exit(0);
757
+ }
758
+ // Compute a simple line-level diff between old block and new block
759
+ const oldEnd = existing.indexOf(endMarker);
760
+ const oldBlockLines = existing.slice(beginIdxNow, oldEnd + endMarker.length).split('\n');
761
+ const newBlockLines = block.split('\n');
762
+ console.log(`Would UPDATE qatools-context block in ${options.output}:`);
763
+ console.log(` Old: ${oldBlockLines.length} lines`);
764
+ console.log(` New: ${newBlockLines.length} lines`);
765
+ console.log(``);
766
+ // Show added/removed lines (simple set diff — ignores reordering)
767
+ const oldSet = new Set(oldBlockLines.map(l => l.trim()).filter(Boolean));
768
+ const newSet = new Set(newBlockLines.map(l => l.trim()).filter(Boolean));
769
+ const added = [...newSet].filter(l => !oldSet.has(l));
770
+ const removed = [...oldSet].filter(l => !newSet.has(l));
771
+ if (removed.length) {
772
+ console.log(` ${removed.length} line(s) removed:`);
773
+ removed.slice(0, 15).forEach(l => console.log(` - ${l.substring(0, 100)}`));
774
+ if (removed.length > 15) console.log(` ... and ${removed.length - 15} more`);
775
+ }
776
+ if (added.length) {
777
+ console.log(` ${added.length} line(s) added:`);
778
+ added.slice(0, 15).forEach(l => console.log(` + ${l.substring(0, 100)}`));
779
+ if (added.length > 15) console.log(` ... and ${added.length - 15} more`);
780
+ }
781
+ console.log(``);
782
+ console.log(`Run without --dry-run to write the changes.`);
783
+ process.exit(0);
784
+ }
785
+
742
786
  if (existing === updated) {
743
787
  console.log(`✓ ${options.output} already in sync (no changes)`);
744
788
  } else {
@@ -1359,9 +1403,74 @@ program
1359
1403
  process.exit(0);
1360
1404
  });
1361
1405
 
1406
+ program
1407
+ .command("context-template")
1408
+ .description("Print an empty App Context JSON template with all fields and example values. Pipe to a file, edit, then context-set.")
1409
+ .option("-o, --output <path>", "Write to file instead of stdout")
1410
+ .action((options) => {
1411
+ const template = {
1412
+ name: "Your project name",
1413
+ purpose: "One sentence: what does this app do and why does it exist?",
1414
+ domainObjects: [
1415
+ "User",
1416
+ "Order",
1417
+ "Product"
1418
+ ],
1419
+ actors: [
1420
+ "End user",
1421
+ "Admin",
1422
+ "External system"
1423
+ ],
1424
+ stack: [
1425
+ { layer: "Frontend", technology: "React", version: "18", notes: "with TypeScript" },
1426
+ { layer: "Backend", technology: "Node.js", version: "20", notes: "Express" },
1427
+ { layer: "Database", technology: "PostgreSQL", version: "16", notes: "" }
1428
+ ],
1429
+ architecture: "Short description of how the system is structured. Where does data flow? What's the main pattern?",
1430
+ security: [
1431
+ "All endpoints require authentication",
1432
+ "Data is isolated per tenant via tenantId column"
1433
+ ],
1434
+ aiRules: [
1435
+ "Build from requirements — never from backlog items",
1436
+ "Evidence-first: cite specific code, test output, or error message",
1437
+ "Never guess — say 'I'm unsure' if evidence is missing",
1438
+ "Acceptance criteria in Given/When/Then on the project's language (sv: Givet/När/Då, en: Given/When/Then) — never mix"
1439
+ ],
1440
+ constraints: [
1441
+ "Hard constraint #1 (e.g. must run offline)",
1442
+ "Hard constraint #2 (e.g. no third-party AI calls)"
1443
+ ],
1444
+ conventions: [
1445
+ { area: "Naming", rule: "camelCase for variables, PascalCase for components" },
1446
+ { area: "Errors", rule: "Always throw with a Result type, never silent fail" }
1447
+ ],
1448
+ environments: [
1449
+ { name: "Production", url: "https://app.example.com", notes: "" },
1450
+ { name: "Staging", url: "https://staging.example.com", notes: "" }
1451
+ ],
1452
+ integrations: [
1453
+ { name: "Stripe", purpose: "Payments" },
1454
+ { name: "SendGrid", purpose: "Transactional email" }
1455
+ ],
1456
+ sections: [
1457
+ { title: "Custom section title", content: "Free-text content for anything that doesn't fit the structured fields above. Use this for project-specific docs, runbooks, glossaries, etc." }
1458
+ ]
1459
+ };
1460
+ const json = JSON.stringify(template, null, 2);
1461
+ if (options.output) {
1462
+ fs.writeFileSync(path.resolve(options.output), json);
1463
+ console.error(`Template written to ${options.output}`);
1464
+ console.error(`Edit it, then upload with: qatools context-set -f ${options.output}`);
1465
+ } else {
1466
+ console.log(json);
1467
+ }
1468
+ process.exit(0);
1469
+ });
1470
+
1362
1471
  program
1363
1472
  .command("context-set")
1364
- .description("Upload App Context from JSON file")
1473
+ .description("Upload App Context from JSON file. Run 'qatools context-template' to see the expected schema.")
1365
1474
  .requiredOption("-f, --file <path>", "Path to App Context JSON file")
1366
1475
  .option("--project <id>", "Target project ID (overrides default; requires access to that project)")
1367
1476
  .option("-e, --email <email>", "Login email")
@@ -1375,6 +1484,13 @@ program
1375
1484
  const saveCtx = httpsCallable(functions, "saveAppContext");
1376
1485
  const result = await saveCtx({ projectId, context: data });
1377
1486
  console.log(`App Context saved (${result.data.id}) — project ${projectId}`);
1487
+ if (result.data.warnings && result.data.warnings.length > 0) {
1488
+ console.error(``);
1489
+ console.error(`⚠ Schema warnings (${result.data.warnings.length}):`);
1490
+ for (const w of result.data.warnings) console.error(` ${w}`);
1491
+ console.error(``);
1492
+ console.error(` These fields were saved but may be ignored. Run 'qatools context-template' to see the schema.`);
1493
+ }
1378
1494
  process.exit(0);
1379
1495
  });
1380
1496
 
@@ -1632,6 +1748,83 @@ program
1632
1748
 
1633
1749
  // 2. Write skills files
1634
1750
  const skills = {
1751
+ "qa-purpose.md": `---
1752
+ description: Read this BEFORE anything else. The purpose of qa-tools and what it expects from you as AI.
1753
+ ---
1754
+
1755
+ # Why qa-tools exists
1756
+
1757
+ qa-tools exists for one reason: to let anyone build production-quality apps with AI that follow their own rules — as if they had built it themselves, just faster.
1758
+
1759
+ Not "demo quality, fast". Not "good enough, fast". **The user's own quality bar, faster.**
1760
+
1761
+ ## What this means for you (the AI)
1762
+
1763
+ You are not a coding assistant. You are a substitute. The user is delegating
1764
+ construction of their app to you, and they expect what comes back to be what
1765
+ they would have built themselves if they had infinite time.
1766
+
1767
+ That standard rules out:
1768
+ - Guessing when verification is possible
1769
+ - "Looks right" without numbers
1770
+ - "User will test it" handoffs of responsibility
1771
+ - Shortcuts the user themselves wouldn't take
1772
+ - Pattern-matched code from training data when the project has its own rules
1773
+
1774
+ ## The chain
1775
+
1776
+ \`\`\`
1777
+ App Context (the user's rules: design, security, AI rules, constraints)
1778
+ ↓
1779
+ Epics (capabilities the user wants)
1780
+ ↓
1781
+ Requirements (Given/When/Then — the user's spec, testable)
1782
+ ↓
1783
+ Code (you build, must trace to requirement)
1784
+ ↓
1785
+ Tests (generated from REQUIREMENTS, not from code)
1786
+ ↓
1787
+ Backlog (signals when chain breaks: AVV / GAP / DEF / IMP)
1788
+ ↓
1789
+ Fix (against the requirement, with --evidence)
1790
+ ↓
1791
+ Requirement marked tested
1792
+ \`\`\`
1793
+
1794
+ Every link is enforceable. The CLI refuses status changes without evidence.
1795
+ The skills refuse vague claims. The model refuses circular fixes (backlog →
1796
+ backlog without going through requirements).
1797
+
1798
+ ## Your starting move
1799
+
1800
+ Before doing ANYTHING in this project:
1801
+ 1. Run \`qatools context\` — read the App Context. This is the user's rule book.
1802
+ 2. Run \`qatools epics\` — see what capabilities exist.
1803
+ 3. Run \`qatools next\` — see what's actually on the agenda.
1804
+
1805
+ If App Context is empty, the project hasn't been set up yet. Help the user
1806
+ draft one before building anything else. **Without App Context you have no
1807
+ basis for being a substitute** — you're just guessing what their rules are.
1808
+
1809
+ ## When you're tempted to skip the chain
1810
+
1811
+ You will be tempted. The user will be impatient. The bug will look obvious.
1812
+ You will think "I'll just fix it and they can verify".
1813
+
1814
+ That is exactly the failure mode qa-tools was built to prevent. The substitute
1815
+ that "just fixes things" is the substitute that drifts away from what the
1816
+ user actually wanted, one shortcut at a time.
1817
+
1818
+ When you feel that temptation, stop. Ask: would the user themselves skip this
1819
+ step if they were doing it manually? If no, you can't either.
1820
+
1821
+ ## Cross-reference
1822
+
1823
+ - qa-loop.md — the mechanics of the loop
1824
+ - qa-evidence-first.md — the core principle (cite, don't guess)
1825
+ - qa-numerical-verification.md — for algorithmic bugs
1826
+ - qa-developer.md — implementation guidance
1827
+ `,
1635
1828
  "qa-loop.md": `---
1636
1829
  description: Overview of the QA-Tools delivery loop — from requirement to verified release. Read this first.
1637
1830
  ---
@@ -1653,6 +1846,22 @@ Requirement → Build → Test → Backlog → Fix → Verify
1653
1846
  5. **AI analyzes and fixes** — AI identifies root cause: is it the code or the requirement? Fixes code or suggests requirement adjustment.
1654
1847
  6. **Retest verifies** — The loop closes only when the requirement is verified.
1655
1848
 
1849
+ ## What this means for you (the AI)
1850
+
1851
+ Each arrow in the loop is a rule on what you can and cannot do:
1852
+
1853
+ **Requirement → Build** means: you don't build until there's an approved requirement. Period. If the user asks you to fix something without a requirement, your first response is "can you formulate what the system should do as a requirement?" — not "sure, I'll fix it".
1854
+
1855
+ **Build → Test** means: the code is not done until there's a test that verifies it meets the requirement. "I compiled it" is not done. "I ran the file and it didn't crash" is not done.
1856
+
1857
+ **Test → Backlog** means: when the test fails, that's a signal to you that either the requirement or the code is wrong. You don't guess which — you investigate. The backlog item is the *observation*, not the diagnosis.
1858
+
1859
+ **Backlog → Fix** means: the fix comes from the requirement, not from the backlog item. The backlog item is the symptom. The requirement is what you build against. If you fix the symptom without going back to the requirement, you're patching, not building.
1860
+
1861
+ **Fix → Verify** means: you don't mark tested without --evidence. The CLI refuses status changes without it. That's not bureaucracy — it's the line between claiming and verifying. If you cannot articulate the verification, you have not verified.
1862
+
1863
+ If any of these feels like friction, that is the system working as intended. The friction is what keeps the chain unbroken.
1864
+
1656
1865
  ## CLI quick reference
1657
1866
 
1658
1867
  \`\`\`bash
@@ -2064,7 +2273,8 @@ If the verification fails: do NOT mark tested. Go back to step 1, find the discr
2064
2273
  for (const [filename, content] of Object.entries(skills)) {
2065
2274
  fs.writeFileSync(path.join(skillsDir, filename), content);
2066
2275
  }
2067
- console.log(" ✓ qa-loop.md (start here)");
2276
+ console.log(" ✓ qa-purpose.md (READ FIRST)");
2277
+ console.log(" ✓ qa-loop.md");
2068
2278
  console.log(" ✓ qa-requirement-analyst.md");
2069
2279
  console.log(" ✓ qa-developer.md");
2070
2280
  console.log(" ✓ qa-test-generator.md");
@@ -2115,23 +2325,32 @@ If the verification fails: do NOT mark tested. Go back to step 1, find the discr
2115
2325
  const qaSection = `
2116
2326
  ## QA-Tools Integration
2117
2327
 
2328
+ This project uses qa-tools (https://qa-tools.com) for requirement-driven AI development. The purpose: build production-quality apps with AI that follow your own rules — as if you built it yourself, just faster. The AI is the builder; you are the requirements author and reviewer.
2329
+
2330
+ **Read \`.claude/skills/qa-purpose.md\` before doing anything in this project.** It explains what qa-tools expects from the AI: not a coding assistant, a substitute for the developer.
2331
+
2118
2332
  \`\`\`bash
2119
2333
  export QATOOLS_API_KEY=${apiKey || "<din-nyckel>"}
2120
2334
  \`\`\`
2121
2335
 
2122
- ### Kommandon
2123
- - \`qatools requirements\` — lista krav
2124
- - \`qatools backlog\` — lista backlog
2125
- - \`qatools pending\` — väntande testsessioner
2126
- - \`qatools read-session -s <id>\` — läs session
2127
- - \`qatools upload-analysis -s <id> -f analysis.json\` — ladda upp analys
2128
- - \`qatools requirement-create -f file.json\` — skapa krav
2129
- - \`qatools template-upload -f file.json\` — ladda upp testprotokoll
2130
- - \`qatools backlog-create -d "..." -t task\` — skapa ärende
2131
- - \`qatools backlog-update -i <id> --status resolved\` — uppdatera
2132
-
2133
- ### Roller (skills)
2134
- Se \`.claude/skills/\` för instruktioner per roll.
2336
+ ### Commands
2337
+ - \`qatools context\` — read the project's rule book (App Context)
2338
+ - \`qatools epics\` — list capabilities
2339
+ - \`qatools next\` — what's on the agenda right now
2340
+ - \`qatools requirements\` — list requirements
2341
+ - \`qatools backlog\` — list backlog signals
2342
+ - \`qatools pending\` — test sessions awaiting analysis
2343
+ - \`qatools read-session -s <id>\` — read session details
2344
+ - \`qatools upload-analysis -s <id> -f analysis.json\` — upload analysis
2345
+ - \`qatools requirement-create -f file.json\` — create requirements
2346
+ - \`qatools requirement-update -i <id> --status tested --evidence "..."\` — mark verified (evidence required)
2347
+ - \`qatools template-upload -f file.json\` — upload test template
2348
+ - \`qatools backlog-create -d "..." -t bug\` — create backlog item
2349
+ - \`qatools backlog-update -i <id> --status resolved --evidence "..."\` — mark resolved (evidence required)
2350
+ - \`qatools access show\` — view access model
2351
+
2352
+ ### Skills
2353
+ See \`.claude/skills/\` for role-specific instructions. Start with \`qa-purpose.md\`.
2135
2354
  `;
2136
2355
 
2137
2356
  if (fs.existsSync(claudeMdPath)) {
@@ -2147,8 +2366,41 @@ Se \`.claude/skills/\` för instruktioner per roll.
2147
2366
  console.log("\n✓ Created CLAUDE.md with QA-Tools section");
2148
2367
  }
2149
2368
 
2150
- console.log("\n🎉 QA-Tools initialized! Claude is ready.");
2151
- if (!apiKey) console.log("\n⚠ No API key set. Add it to CLAUDE.md: export QATOOLS_API_KEY=<key>");
2369
+ // 5. Final purpose-first banner — check App Context state and tell the AI/user what to do
2370
+ let contextStateMessage = "";
2371
+ try {
2372
+ const creds = getCredentials();
2373
+ if (creds?.projectId) {
2374
+ const getCtx = httpsCallable(functions, "getAppContext");
2375
+ const ctxResult = await getCtx({ projectId: creds.projectId });
2376
+ const ctx = ctxResult.data.appContext;
2377
+ if (!ctx || (!ctx.purpose && !(ctx.stack || []).length && !(ctx.aiRules || []).length)) {
2378
+ contextStateMessage = "\n⚠ App Context is empty for this project.\n Without it, the AI has no basis for substituting for you.\n First step: open https://qa-tools.com and define App Context (purpose, stack, conventions, AI rules)\n or upload a JSON via: qatools context-set -f context.json\n";
2379
+ } else {
2380
+ const lastSync = ctx.updatedAt?._seconds ? new Date(ctx.updatedAt._seconds * 1000).toISOString().split('T')[0] : 'unknown';
2381
+ contextStateMessage = `\n✓ App Context loaded (last updated: ${lastSync})\n`;
2382
+ }
2383
+ }
2384
+ } catch (e) {
2385
+ // Best-effort — don't block init on this check
2386
+ }
2387
+
2388
+ console.log(`
2389
+ 🎉 QA-Tools initialized!
2390
+
2391
+ ⚠ READ THIS FIRST: .claude/skills/qa-purpose.md
2392
+
2393
+ qa-tools exists to let anyone build production-quality apps with AI
2394
+ that follow their own rules — as if you built it yourself, just faster.
2395
+ The AI is not an assistant. It is a substitute for the developer.
2396
+ Read qa-purpose.md before doing anything else in this project.
2397
+ ${contextStateMessage}
2398
+ Next steps for the AI in this project:
2399
+ 1. qatools context — read the project's rule book
2400
+ 2. qatools epics — see what capabilities exist
2401
+ 3. qatools next — see what's on the agenda
2402
+ `);
2403
+ if (!apiKey) console.log("⚠ No API key set. Add it to CLAUDE.md: export QATOOLS_API_KEY=<key>\n");
2152
2404
  process.exit(0);
2153
2405
  });
2154
2406