qatools-cli 1.1.5 → 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.
- package/package.json +1 -1
- package/qatools.js +117 -1
package/package.json
CHANGED
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
|
|