qatools-cli 1.1.3 → 1.1.5

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 +270 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qatools-cli",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
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
@@ -903,6 +903,14 @@ program
903
903
  next: `qatools requirement-update -i <id> --status approved (or refine AC first)`,
904
904
  });
905
905
  }
906
+ if (data.agenda.accessModelMissing) {
907
+ sections.push({
908
+ icon: "⚪",
909
+ title: "No access model defined for this project",
910
+ items: [" • Who can sign in? Which roles exist? What's protected? How is data isolated?"],
911
+ next: `qatools access show (shows a minimal example) → qatools access set -f model.json`,
912
+ });
913
+ }
906
914
 
907
915
  if (sections.length === 0) {
908
916
  console.log(" ✓ Nothing on the agenda. The project is in a clean state.\n");
@@ -1370,6 +1378,116 @@ program
1370
1378
  process.exit(0);
1371
1379
  });
1372
1380
 
1381
+ // access subcommands grouped under 'access'
1382
+ const accessCmd = program
1383
+ .command("access")
1384
+ .description("Manage the project's Access Model (who-can-do-what, structured)");
1385
+
1386
+ accessCmd
1387
+ .command("show")
1388
+ .description("Show the access model for the project")
1389
+ .option("--project <id>", "Target project ID (overrides default)")
1390
+ .option("-o, --output <path>", "Save to JSON file")
1391
+ .option("--json", "Output JSON to stdout")
1392
+ .option("-e, --email <email>", "Login email")
1393
+ .option("-p, --password <password>", "Login password")
1394
+ .action(async (options) => {
1395
+ await authenticate(options);
1396
+ const creds = getCredentials();
1397
+ const projectId = options.project || creds?.projectId;
1398
+ if (!projectId) { console.error("No project configured. Run: qatools setup, or pass --project <id>"); process.exit(1); }
1399
+ const getModel = httpsCallable(functions, "getAccessModel");
1400
+ let result;
1401
+ try { result = await getModel({ projectId }); }
1402
+ catch (e) { console.error("Failed:", e.message); process.exit(1); }
1403
+ const model = result.data.accessModel;
1404
+ if (!model) {
1405
+ console.log(`No access model defined for this project.`);
1406
+ console.log(`Define one by writing a JSON file and running: qatools access set -f model.json`);
1407
+ console.log(``);
1408
+ console.log(`Minimal shape:`);
1409
+ console.log(JSON.stringify({
1410
+ authMethods: [{ key: "google", description: "Google OAuth", primary: true }],
1411
+ roles: [{ key: "owner", description: "Project owner with full control" }],
1412
+ resources: [{ key: "project", description: "Project metadata", ownedBy: "project", permissions: [{ role: "owner", actions: ["read","create","update","delete"] }] }],
1413
+ isolationRules: [{ scope: "project", description: "Data scoped by projectId field", enforcedBy: ["firestore.rules", "checkProjectAccess()"] }],
1414
+ }, null, 2));
1415
+ process.exit(0);
1416
+ }
1417
+ if (options.output) {
1418
+ fs.writeFileSync(path.resolve(options.output), JSON.stringify(model, null, 2));
1419
+ console.log(`Saved to ${options.output}`);
1420
+ process.exit(0);
1421
+ }
1422
+ if (options.json) {
1423
+ console.log(JSON.stringify(model, null, 2));
1424
+ process.exit(0);
1425
+ }
1426
+ // Pretty text output
1427
+ console.log(``);
1428
+ console.log(`Access Model — project ${projectId}`);
1429
+ console.log(``);
1430
+ if (model.authMethods?.length) {
1431
+ console.log(`AUTH METHODS`);
1432
+ for (const a of model.authMethods) console.log(` ${a.primary ? "*" : " "} ${a.key.padEnd(15)} ${a.description}`);
1433
+ console.log(``);
1434
+ }
1435
+ if (model.roles?.length) {
1436
+ console.log(`ROLES`);
1437
+ for (const r of model.roles) {
1438
+ const inh = r.inherits?.length ? ` (inherits: ${r.inherits.join(", ")})` : "";
1439
+ console.log(` ${r.key.padEnd(15)} ${r.description}${inh}`);
1440
+ }
1441
+ console.log(``);
1442
+ }
1443
+ if (model.resources?.length) {
1444
+ console.log(`RESOURCES`);
1445
+ for (const r of model.resources) {
1446
+ const coll = r.collection ? ` [${r.collection}]` : "";
1447
+ console.log(` ${r.key}${coll} — owned by ${r.ownedBy}`);
1448
+ console.log(` ${r.description}`);
1449
+ for (const p of (r.permissions || [])) {
1450
+ const cond = p.condition ? ` (${p.condition})` : "";
1451
+ console.log(` ${p.role.padEnd(15)} ${p.actions.join(",")}${cond}`);
1452
+ }
1453
+ }
1454
+ console.log(``);
1455
+ }
1456
+ if (model.isolationRules?.length) {
1457
+ console.log(`ISOLATION RULES`);
1458
+ for (const i of model.isolationRules) {
1459
+ console.log(` [${i.scope}] ${i.description}`);
1460
+ if (i.enforcedBy?.length) console.log(` enforced by: ${i.enforcedBy.join(", ")}`);
1461
+ }
1462
+ console.log(``);
1463
+ }
1464
+ process.exit(0);
1465
+ });
1466
+
1467
+ accessCmd
1468
+ .command("set")
1469
+ .description("Upload (replace) the entire access model from a JSON file")
1470
+ .requiredOption("-f, --file <path>", "Path to access model JSON")
1471
+ .option("--project <id>", "Target project ID (overrides default)")
1472
+ .option("-e, --email <email>", "Login email")
1473
+ .option("-p, --password <password>", "Login password")
1474
+ .action(async (options) => {
1475
+ await authenticate(options);
1476
+ const creds = getCredentials();
1477
+ const projectId = options.project || creds?.projectId;
1478
+ if (!projectId) { console.error("No project configured. Run: qatools setup, or pass --project <id>"); process.exit(1); }
1479
+ const model = JSON.parse(fs.readFileSync(path.resolve(options.file), "utf8"));
1480
+ const saveModel = httpsCallable(functions, "saveAccessModel");
1481
+ try {
1482
+ const result = await saveModel({ projectId, model });
1483
+ console.log(`Access model saved (${result.data.id}) — project ${projectId}`);
1484
+ } catch (e) {
1485
+ console.error("Failed:", e.message);
1486
+ process.exit(1);
1487
+ }
1488
+ process.exit(0);
1489
+ });
1490
+
1373
1491
  program
1374
1492
  .command("epics")
1375
1493
  .description("List epics for the project")
@@ -1514,6 +1632,83 @@ program
1514
1632
 
1515
1633
  // 2. Write skills files
1516
1634
  const skills = {
1635
+ "qa-purpose.md": `---
1636
+ description: Read this BEFORE anything else. The purpose of qa-tools and what it expects from you as AI.
1637
+ ---
1638
+
1639
+ # Why qa-tools exists
1640
+
1641
+ 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.
1642
+
1643
+ Not "demo quality, fast". Not "good enough, fast". **The user's own quality bar, faster.**
1644
+
1645
+ ## What this means for you (the AI)
1646
+
1647
+ You are not a coding assistant. You are a substitute. The user is delegating
1648
+ construction of their app to you, and they expect what comes back to be what
1649
+ they would have built themselves if they had infinite time.
1650
+
1651
+ That standard rules out:
1652
+ - Guessing when verification is possible
1653
+ - "Looks right" without numbers
1654
+ - "User will test it" handoffs of responsibility
1655
+ - Shortcuts the user themselves wouldn't take
1656
+ - Pattern-matched code from training data when the project has its own rules
1657
+
1658
+ ## The chain
1659
+
1660
+ \`\`\`
1661
+ App Context (the user's rules: design, security, AI rules, constraints)
1662
+ ↓
1663
+ Epics (capabilities the user wants)
1664
+ ↓
1665
+ Requirements (Given/When/Then — the user's spec, testable)
1666
+ ↓
1667
+ Code (you build, must trace to requirement)
1668
+ ↓
1669
+ Tests (generated from REQUIREMENTS, not from code)
1670
+ ↓
1671
+ Backlog (signals when chain breaks: AVV / GAP / DEF / IMP)
1672
+ ↓
1673
+ Fix (against the requirement, with --evidence)
1674
+ ↓
1675
+ Requirement marked tested
1676
+ \`\`\`
1677
+
1678
+ Every link is enforceable. The CLI refuses status changes without evidence.
1679
+ The skills refuse vague claims. The model refuses circular fixes (backlog →
1680
+ backlog without going through requirements).
1681
+
1682
+ ## Your starting move
1683
+
1684
+ Before doing ANYTHING in this project:
1685
+ 1. Run \`qatools context\` — read the App Context. This is the user's rule book.
1686
+ 2. Run \`qatools epics\` — see what capabilities exist.
1687
+ 3. Run \`qatools next\` — see what's actually on the agenda.
1688
+
1689
+ If App Context is empty, the project hasn't been set up yet. Help the user
1690
+ draft one before building anything else. **Without App Context you have no
1691
+ basis for being a substitute** — you're just guessing what their rules are.
1692
+
1693
+ ## When you're tempted to skip the chain
1694
+
1695
+ You will be tempted. The user will be impatient. The bug will look obvious.
1696
+ You will think "I'll just fix it and they can verify".
1697
+
1698
+ That is exactly the failure mode qa-tools was built to prevent. The substitute
1699
+ that "just fixes things" is the substitute that drifts away from what the
1700
+ user actually wanted, one shortcut at a time.
1701
+
1702
+ When you feel that temptation, stop. Ask: would the user themselves skip this
1703
+ step if they were doing it manually? If no, you can't either.
1704
+
1705
+ ## Cross-reference
1706
+
1707
+ - qa-loop.md — the mechanics of the loop
1708
+ - qa-evidence-first.md — the core principle (cite, don't guess)
1709
+ - qa-numerical-verification.md — for algorithmic bugs
1710
+ - qa-developer.md — implementation guidance
1711
+ `,
1517
1712
  "qa-loop.md": `---
1518
1713
  description: Overview of the QA-Tools delivery loop — from requirement to verified release. Read this first.
1519
1714
  ---
@@ -1535,6 +1730,22 @@ Requirement → Build → Test → Backlog → Fix → Verify
1535
1730
  5. **AI analyzes and fixes** — AI identifies root cause: is it the code or the requirement? Fixes code or suggests requirement adjustment.
1536
1731
  6. **Retest verifies** — The loop closes only when the requirement is verified.
1537
1732
 
1733
+ ## What this means for you (the AI)
1734
+
1735
+ Each arrow in the loop is a rule on what you can and cannot do:
1736
+
1737
+ **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".
1738
+
1739
+ **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.
1740
+
1741
+ **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.
1742
+
1743
+ **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.
1744
+
1745
+ **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.
1746
+
1747
+ If any of these feels like friction, that is the system working as intended. The friction is what keeps the chain unbroken.
1748
+
1538
1749
  ## CLI quick reference
1539
1750
 
1540
1751
  \`\`\`bash
@@ -1946,7 +2157,8 @@ If the verification fails: do NOT mark tested. Go back to step 1, find the discr
1946
2157
  for (const [filename, content] of Object.entries(skills)) {
1947
2158
  fs.writeFileSync(path.join(skillsDir, filename), content);
1948
2159
  }
1949
- console.log(" ✓ qa-loop.md (start here)");
2160
+ console.log(" ✓ qa-purpose.md (READ FIRST)");
2161
+ console.log(" ✓ qa-loop.md");
1950
2162
  console.log(" ✓ qa-requirement-analyst.md");
1951
2163
  console.log(" ✓ qa-developer.md");
1952
2164
  console.log(" ✓ qa-test-generator.md");
@@ -1997,23 +2209,32 @@ If the verification fails: do NOT mark tested. Go back to step 1, find the discr
1997
2209
  const qaSection = `
1998
2210
  ## QA-Tools Integration
1999
2211
 
2212
+ 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.
2213
+
2214
+ **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.
2215
+
2000
2216
  \`\`\`bash
2001
2217
  export QATOOLS_API_KEY=${apiKey || "<din-nyckel>"}
2002
2218
  \`\`\`
2003
2219
 
2004
- ### Kommandon
2005
- - \`qatools requirements\` — lista krav
2006
- - \`qatools backlog\` — lista backlog
2007
- - \`qatools pending\` — väntande testsessioner
2008
- - \`qatools read-session -s <id>\` — läs session
2009
- - \`qatools upload-analysis -s <id> -f analysis.json\` — ladda upp analys
2010
- - \`qatools requirement-create -f file.json\` — skapa krav
2011
- - \`qatools template-upload -f file.json\` — ladda upp testprotokoll
2012
- - \`qatools backlog-create -d "..." -t task\` — skapa ärende
2013
- - \`qatools backlog-update -i <id> --status resolved\` — uppdatera
2014
-
2015
- ### Roller (skills)
2016
- Se \`.claude/skills/\` för instruktioner per roll.
2220
+ ### Commands
2221
+ - \`qatools context\` — read the project's rule book (App Context)
2222
+ - \`qatools epics\` — list capabilities
2223
+ - \`qatools next\` — what's on the agenda right now
2224
+ - \`qatools requirements\` — list requirements
2225
+ - \`qatools backlog\` — list backlog signals
2226
+ - \`qatools pending\` — test sessions awaiting analysis
2227
+ - \`qatools read-session -s <id>\` — read session details
2228
+ - \`qatools upload-analysis -s <id> -f analysis.json\` — upload analysis
2229
+ - \`qatools requirement-create -f file.json\` — create requirements
2230
+ - \`qatools requirement-update -i <id> --status tested --evidence "..."\` — mark verified (evidence required)
2231
+ - \`qatools template-upload -f file.json\` — upload test template
2232
+ - \`qatools backlog-create -d "..." -t bug\` — create backlog item
2233
+ - \`qatools backlog-update -i <id> --status resolved --evidence "..."\` — mark resolved (evidence required)
2234
+ - \`qatools access show\` — view access model
2235
+
2236
+ ### Skills
2237
+ See \`.claude/skills/\` for role-specific instructions. Start with \`qa-purpose.md\`.
2017
2238
  `;
2018
2239
 
2019
2240
  if (fs.existsSync(claudeMdPath)) {
@@ -2029,8 +2250,41 @@ Se \`.claude/skills/\` för instruktioner per roll.
2029
2250
  console.log("\n✓ Created CLAUDE.md with QA-Tools section");
2030
2251
  }
2031
2252
 
2032
- console.log("\n🎉 QA-Tools initialized! Claude is ready.");
2033
- if (!apiKey) console.log("\n⚠ No API key set. Add it to CLAUDE.md: export QATOOLS_API_KEY=<key>");
2253
+ // 5. Final purpose-first banner — check App Context state and tell the AI/user what to do
2254
+ let contextStateMessage = "";
2255
+ try {
2256
+ const creds = getCredentials();
2257
+ if (creds?.projectId) {
2258
+ const getCtx = httpsCallable(functions, "getAppContext");
2259
+ const ctxResult = await getCtx({ projectId: creds.projectId });
2260
+ const ctx = ctxResult.data.appContext;
2261
+ if (!ctx || (!ctx.purpose && !(ctx.stack || []).length && !(ctx.aiRules || []).length)) {
2262
+ 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";
2263
+ } else {
2264
+ const lastSync = ctx.updatedAt?._seconds ? new Date(ctx.updatedAt._seconds * 1000).toISOString().split('T')[0] : 'unknown';
2265
+ contextStateMessage = `\n✓ App Context loaded (last updated: ${lastSync})\n`;
2266
+ }
2267
+ }
2268
+ } catch (e) {
2269
+ // Best-effort — don't block init on this check
2270
+ }
2271
+
2272
+ console.log(`
2273
+ 🎉 QA-Tools initialized!
2274
+
2275
+ ⚠ READ THIS FIRST: .claude/skills/qa-purpose.md
2276
+
2277
+ qa-tools exists to let anyone build production-quality apps with AI
2278
+ that follow their own rules — as if you built it yourself, just faster.
2279
+ The AI is not an assistant. It is a substitute for the developer.
2280
+ Read qa-purpose.md before doing anything else in this project.
2281
+ ${contextStateMessage}
2282
+ Next steps for the AI in this project:
2283
+ 1. qatools context — read the project's rule book
2284
+ 2. qatools epics — see what capabilities exist
2285
+ 3. qatools next — see what's on the agenda
2286
+ `);
2287
+ if (!apiKey) console.log("⚠ No API key set. Add it to CLAUDE.md: export QATOOLS_API_KEY=<key>\n");
2034
2288
  process.exit(0);
2035
2289
  });
2036
2290