skilldrop-cli 0.16.29 → 0.16.30

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 (174) hide show
  1. package/bin/skilldrop.js +160 -33
  2. package/catalogue.json +7 -3
  3. package/model-routing.json +16 -0
  4. package/package.json +1 -1
  5. package/packs/ai-engineering/skills/mcp-pairing/SKILL.md +93 -0
  6. package/packs/ai-engineering/skills/mcp-pairing/evals/eval_queries.json +13 -0
  7. package/packs/ai-engineering/skills/mcp-pairing/evals/evals.json +76 -0
  8. package/packs/ai-engineering/skills/mcp-pairing/evals/files/2/mcp.json +32 -0
  9. package/packs/ai-engineering/skills/mcp-pairing/evals/files/4/.mcp.json +47 -0
  10. package/packs/ai-engineering/skills/mcp-pairing/evals/files/4/docs/pr-loop.md +12 -0
  11. package/packs/ai-engineering/skills/mcp-pairing/evals/files/5/docs/oncall-triage.md +18 -0
  12. package/packs/ai-engineering/skills/mcp-pairing/examples/build-loop-github-jira-datadog.md +69 -0
  13. package/packs/ai-engineering/skills/mcp-pairing/manifest.json +17 -0
  14. package/packs/ai-engineering/skills/mcp-pairing/reference.md +136 -0
  15. package/packs/ai-engineering/skills/mcp-pairing/templates/mcp-pairing.md +43 -0
  16. package/packs/dev-team/loops/build/LOOP.md +4 -2
  17. package/packs/dev-team/loops/build/loop.json +2 -2
  18. package/packs/dev-team/skills/codebase-tour/SKILL.md +115 -0
  19. package/packs/dev-team/skills/codebase-tour/evals/eval_queries.json +13 -0
  20. package/packs/dev-team/skills/codebase-tour/evals/evals.json +77 -0
  21. package/packs/dev-team/skills/codebase-tour/evals/files/1/.github/workflows/ci.yml +23 -0
  22. package/packs/dev-team/skills/codebase-tour/evals/files/1/README.md +23 -0
  23. package/packs/dev-team/skills/codebase-tour/evals/files/1/openapi.yaml +21 -0
  24. package/packs/dev-team/skills/codebase-tour/evals/files/1/package-lock.json +7 -0
  25. package/packs/dev-team/skills/codebase-tour/evals/files/1/package.json +29 -0
  26. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/config.ts +20 -0
  27. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/domain/shipments/filter.ts +24 -0
  28. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/domain/shipments/repository.ts +15 -0
  29. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/errors.ts +9 -0
  30. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/generated/api-types.ts +9 -0
  31. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/http/handlers/shipments.ts +38 -0
  32. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/http/routes.ts +7 -0
  33. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/jobs/export-shipments.ts +22 -0
  34. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/server.ts +18 -0
  35. package/packs/dev-team/skills/codebase-tour/evals/files/1/tests/domain/filter.test.ts +16 -0
  36. package/packs/dev-team/skills/codebase-tour/evals/files/1/tests/http/shipments.test.ts +18 -0
  37. package/packs/dev-team/skills/codebase-tour/evals/files/1/tsconfig.json +10 -0
  38. package/packs/dev-team/skills/codebase-tour/evals/files/1/vitest.config.ts +5 -0
  39. package/packs/dev-team/skills/codebase-tour/evals/files/2/.github/workflows/ci.yml +16 -0
  40. package/packs/dev-team/skills/codebase-tour/evals/files/2/README.md +16 -0
  41. package/packs/dev-team/skills/codebase-tour/evals/files/2/noxfile.py +14 -0
  42. package/packs/dev-team/skills/codebase-tour/evals/files/2/pyproject.toml +22 -0
  43. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/__init__.py +3 -0
  44. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/_version.py +4 -0
  45. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/cli.py +32 -0
  46. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/detect.py +15 -0
  47. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/errors.py +2 -0
  48. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/money.py +14 -0
  49. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/__init__.py +8 -0
  50. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/acme.py +18 -0
  51. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/base.py +24 -0
  52. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/harbor.py +13 -0
  53. package/packs/dev-team/skills/codebase-tour/evals/files/2/tests/fixtures/acme_sample.csv +3 -0
  54. package/packs/dev-team/skills/codebase-tour/evals/files/2/tests/test_acme.py +23 -0
  55. package/packs/dev-team/skills/codebase-tour/evals/files/2/tests/test_detect.py +13 -0
  56. package/packs/dev-team/skills/codebase-tour/evals/files/3/.github/workflows/ci.yml +28 -0
  57. package/packs/dev-team/skills/codebase-tour/evals/files/3/Makefile +16 -0
  58. package/packs/dev-team/skills/codebase-tour/evals/files/3/README.md +16 -0
  59. package/packs/dev-team/skills/codebase-tour/evals/files/3/cmd/tidepool/main.go +24 -0
  60. package/packs/dev-team/skills/codebase-tour/evals/files/3/go.mod +5 -0
  61. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/config/config.go +38 -0
  62. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/dispatch/dispatcher.go +52 -0
  63. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/dispatch/dispatcher_test.go +42 -0
  64. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/dispatch/sender.go +32 -0
  65. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/replay/replay.go +31 -0
  66. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/db.go +35 -0
  67. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/query.sql +11 -0
  68. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/query.sql.go +30 -0
  69. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/schema.sql +6 -0
  70. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/store_integration_test.go +21 -0
  71. package/packs/dev-team/skills/codebase-tour/evals/files/3/sqlc.yaml +9 -0
  72. package/packs/dev-team/skills/codebase-tour/evals/files/4/.env.example +4 -0
  73. package/packs/dev-team/skills/codebase-tour/evals/files/4/.github/workflows/ci.yml +16 -0
  74. package/packs/dev-team/skills/codebase-tour/evals/files/4/Makefile +16 -0
  75. package/packs/dev-team/skills/codebase-tour/evals/files/4/README.md +23 -0
  76. package/packs/dev-team/skills/codebase-tour/evals/files/4/data/rates.csv +4 -0
  77. package/packs/dev-team/skills/codebase-tour/evals/files/4/pyproject.toml +19 -0
  78. package/packs/dev-team/skills/codebase-tour/evals/files/4/scripts/gen_rates.py +29 -0
  79. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/__init__.py +0 -0
  80. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/api/__init__.py +0 -0
  81. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/api/preview.py +14 -0
  82. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/api/routes.py +36 -0
  83. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/config.py +15 -0
  84. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/discounts.py +8 -0
  85. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/generated/__init__.py +0 -0
  86. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/generated/rates_table.py +8 -0
  87. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/main.py +8 -0
  88. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/shipping.py +18 -0
  89. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/tax.py +7 -0
  90. package/packs/dev-team/skills/codebase-tour/evals/files/4/tests/conftest.py +3 -0
  91. package/packs/dev-team/skills/codebase-tour/evals/files/4/tests/unit/test_discounts.py +11 -0
  92. package/packs/dev-team/skills/codebase-tour/evals/files/4/tests/unit/test_tax.py +13 -0
  93. package/packs/dev-team/skills/codebase-tour/evals/files/5/.env.example +3 -0
  94. package/packs/dev-team/skills/codebase-tour/evals/files/5/.github/workflows/ci.yml +13 -0
  95. package/packs/dev-team/skills/codebase-tour/evals/files/5/README.md +18 -0
  96. package/packs/dev-team/skills/codebase-tour/evals/files/5/package.json +13 -0
  97. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/openapi.json +30 -0
  98. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/package.json +15 -0
  99. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/db.js +19 -0
  100. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/flags.js +8 -0
  101. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/generated/schema.js +32 -0
  102. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/middleware/validate.js +20 -0
  103. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/routes/bookings.js +15 -0
  104. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/server.js +15 -0
  105. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/services/bookings.js +23 -0
  106. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/services/pricing.js +9 -0
  107. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/test/pricing.test.js +15 -0
  108. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/package.json +9 -0
  109. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/src/bookingForm.js +11 -0
  110. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/src/format.js +7 -0
  111. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/test/format.test.js +12 -0
  112. package/packs/dev-team/skills/codebase-tour/evals/files/5/scripts/gen-schema.mjs +27 -0
  113. package/packs/dev-team/skills/codebase-tour/examples/refund-window.md +158 -0
  114. package/packs/dev-team/skills/codebase-tour/manifest.json +47 -0
  115. package/packs/dev-team/skills/codebase-tour/scripts/repo_facts.py +403 -0
  116. package/packs/dev-team/skills/codebase-tour/templates/codebase-tour.md +85 -0
  117. package/packs/dev-team/skills/dependency-upgrade-review/SKILL.md +101 -0
  118. package/packs/dev-team/skills/dependency-upgrade-review/evals/eval_queries.json +11 -0
  119. package/packs/dev-team/skills/dependency-upgrade-review/evals/evals.json +77 -0
  120. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/.github/workflows/ci.yml +20 -0
  121. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/pr.diff +87 -0
  122. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/quillmark-CHANGELOG.md +24 -0
  123. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/src/render/comment.js +6 -0
  124. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/src/render/note.js +8 -0
  125. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/test/note.test.js +12 -0
  126. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/.github/workflows/ci.yml +23 -0
  127. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/.github/workflows/release.yml +23 -0
  128. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/pr.diff +32 -0
  129. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/release-notes.md +20 -0
  130. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/.github/workflows/ci.yml +18 -0
  131. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/Dockerfile +8 -0
  132. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/PR.md +36 -0
  133. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/pr.diff +47 -0
  134. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/src/ingest/client.py +24 -0
  135. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/src/ingest/reader.py +32 -0
  136. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/tests/test_reader.py +15 -0
  137. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/upstream/quillstream-CHANGELOG.md +37 -0
  138. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/.github/workflows/ci.yml +22 -0
  139. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/.github/workflows/release.yml +25 -0
  140. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/.nvmrc +1 -0
  141. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/PR-412.md +16 -0
  142. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/PR-413.md +15 -0
  143. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/package.json +21 -0
  144. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/pr-412.diff +26 -0
  145. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/pr-413.diff +84 -0
  146. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/scripts/release.mjs +18 -0
  147. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/upstream/release-notary-CHANGELOG.md +16 -0
  148. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/upstream/setup-toolchain-RELEASES.md +22 -0
  149. package/packs/dev-team/skills/dependency-upgrade-review/examples/inputs/ledgerline.diff +38 -0
  150. package/packs/dev-team/skills/dependency-upgrade-review/examples/ledgerline-tidyparse.md +91 -0
  151. package/packs/dev-team/skills/dependency-upgrade-review/manifest.json +50 -0
  152. package/packs/dev-team/skills/dependency-upgrade-review/reference.md +99 -0
  153. package/packs/dev-team/skills/dependency-upgrade-review/scripts/upgrade_diff.py +718 -0
  154. package/packs/product-manager/loops/discover/LOOP.md +5 -4
  155. package/packs/product-manager/loops/discover/loop.json +2 -2
  156. package/packs/product-manager/skills/feedback-synthesis/SKILL.md +182 -0
  157. package/packs/product-manager/skills/feedback-synthesis/evals/eval_queries.json +46 -0
  158. package/packs/product-manager/skills/feedback-synthesis/evals/evals.json +76 -0
  159. package/packs/product-manager/skills/feedback-synthesis/evals/files/1/interview-notes.md +47 -0
  160. package/packs/product-manager/skills/feedback-synthesis/evals/files/1/nps-verbatims.csv +13 -0
  161. package/packs/product-manager/skills/feedback-synthesis/evals/files/1/support-tickets.csv +36 -0
  162. package/packs/product-manager/skills/feedback-synthesis/evals/files/3/churn-exit-survey.csv +11 -0
  163. package/packs/product-manager/skills/feedback-synthesis/evals/files/3/sales-call-notes.md +55 -0
  164. package/packs/product-manager/skills/feedback-synthesis/evals/files/4/csat_comments_q3.csv +17 -0
  165. package/packs/product-manager/skills/feedback-synthesis/evals/files/4/support_tickets_q3.csv +49 -0
  166. package/packs/product-manager/skills/feedback-synthesis/evals/files/5/app_reviews_last_60_days.csv +32 -0
  167. package/packs/product-manager/skills/feedback-synthesis/evals/files/5/interview_notes_round1.md +48 -0
  168. package/packs/product-manager/skills/feedback-synthesis/examples/booking-app-feedback.md +139 -0
  169. package/packs/product-manager/skills/feedback-synthesis/examples/inputs/booking-app-coded.csv +17 -0
  170. package/packs/product-manager/skills/feedback-synthesis/manifest.json +53 -0
  171. package/packs/product-manager/skills/feedback-synthesis/reference.md +89 -0
  172. package/packs/product-manager/skills/feedback-synthesis/scripts/theme_counts.py +355 -0
  173. package/packs/product-manager/skills/feedback-synthesis/templates/coding-sheet.csv +6 -0
  174. package/packs/product-manager/skills/feedback-synthesis/templates/synthesis.md +42 -0
@@ -0,0 +1,30 @@
1
+ {
2
+ "openapi": "3.0.3",
3
+ "info": { "title": "cabinbook", "version": "1.4.0" },
4
+ "paths": {
5
+ "/bookings": {
6
+ "post": {
7
+ "requestBody": {
8
+ "content": {
9
+ "application/json": { "schema": { "$ref": "#/components/schemas/BookingRequest" } }
10
+ }
11
+ },
12
+ "responses": { "201": { "description": "Created" } }
13
+ }
14
+ }
15
+ },
16
+ "components": {
17
+ "schemas": {
18
+ "BookingRequest": {
19
+ "type": "object",
20
+ "required": ["cabinId", "checkIn", "nights"],
21
+ "properties": {
22
+ "cabinId": { "type": "string" },
23
+ "checkIn": { "type": "string", "format": "date" },
24
+ "nights": { "type": "integer", "minimum": 1, "maximum": 14 },
25
+ "guests": { "type": "integer", "minimum": 1, "maximum": 8 }
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@cabinbook/api",
3
+ "version": "1.4.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/server.js",
7
+ "scripts": {
8
+ "dev": "node --env-file=../../.env --watch src/server.js",
9
+ "start": "node src/server.js",
10
+ "test": "node --test"
11
+ },
12
+ "dependencies": {
13
+ "express": "^4.19.2"
14
+ }
15
+ }
@@ -0,0 +1,19 @@
1
+ // In production this is a pg pool built from DATABASE_URL; locally an in-memory stand-in.
2
+ const url = process.env.DATABASE_URL;
3
+ if (!url) {
4
+ throw new Error('DATABASE_URL is not set. Copy .env.example to .env.');
5
+ }
6
+
7
+ const bookings = [];
8
+
9
+ export const db = {
10
+ url,
11
+ async insertBooking(row) {
12
+ const saved = { id: String(bookings.length + 1), ...row };
13
+ bookings.push(saved);
14
+ return saved;
15
+ },
16
+ async bookingsForCabin(cabinId) {
17
+ return bookings.filter((b) => b.cabinId === cabinId);
18
+ },
19
+ };
@@ -0,0 +1,8 @@
1
+ // Feature flags come from a comma-separated env var, e.g. FEATURE_FLAGS=long-stays,new-pricing
2
+ const enabled = new Set(
3
+ (process.env.FEATURE_FLAGS ?? '').split(',').map((f) => f.trim()).filter(Boolean),
4
+ );
5
+
6
+ export function isEnabled(flag) {
7
+ return enabled.has(flag);
8
+ }
@@ -0,0 +1,32 @@
1
+ // DO NOT EDIT. Generated by scripts/gen-schema.mjs from packages/api/openapi.json.
2
+ // Change openapi.json and run `npm run gen`.
3
+
4
+ export const schemas = {
5
+ "BookingRequest": {
6
+ "type": "object",
7
+ "required": [
8
+ "cabinId",
9
+ "checkIn",
10
+ "nights"
11
+ ],
12
+ "properties": {
13
+ "cabinId": {
14
+ "type": "string"
15
+ },
16
+ "checkIn": {
17
+ "type": "string",
18
+ "format": "date"
19
+ },
20
+ "nights": {
21
+ "type": "integer",
22
+ "minimum": 1,
23
+ "maximum": 14
24
+ },
25
+ "guests": {
26
+ "type": "integer",
27
+ "minimum": 1,
28
+ "maximum": 8
29
+ }
30
+ }
31
+ }
32
+ };
@@ -0,0 +1,20 @@
1
+ import { schemas } from '../generated/schema.js';
2
+
3
+ export function validate(schemaName) {
4
+ const schema = schemas[schemaName];
5
+ return (req, res, next) => {
6
+ const errors = [];
7
+ for (const field of schema.required ?? []) {
8
+ if (req.body?.[field] === undefined) errors.push(`${field} is required`);
9
+ }
10
+ for (const [field, rule] of Object.entries(schema.properties)) {
11
+ const value = req.body?.[field];
12
+ if (value === undefined) continue;
13
+ if (rule.type === 'integer' && !Number.isInteger(value)) errors.push(`${field} must be an integer`);
14
+ if (rule.minimum !== undefined && value < rule.minimum) errors.push(`${field} must be >= ${rule.minimum}`);
15
+ if (rule.maximum !== undefined && value > rule.maximum) errors.push(`${field} must be <= ${rule.maximum}`);
16
+ }
17
+ if (errors.length) return res.status(400).json({ errors });
18
+ next();
19
+ };
20
+ }
@@ -0,0 +1,15 @@
1
+ import { Router } from 'express';
2
+ import { validate } from '../middleware/validate.js';
3
+ import { createBooking } from '../services/bookings.js';
4
+
5
+ export const bookingsRouter = Router();
6
+
7
+ bookingsRouter.post('/bookings', validate('BookingRequest'), async (req, res, next) => {
8
+ try {
9
+ const booking = await createBooking(req.body, req.member);
10
+ res.status(201).json(booking);
11
+ } catch (err) {
12
+ if (err.status) return res.status(err.status).json({ error: err.message });
13
+ next(err);
14
+ }
15
+ });
@@ -0,0 +1,15 @@
1
+ import express from 'express';
2
+ import { bookingsRouter } from './routes/bookings.js';
3
+
4
+ const app = express();
5
+ app.use(express.json());
6
+ app.use((req, _res, next) => {
7
+ // Members are identified by the upstream proxy.
8
+ const id = req.get('x-member-id');
9
+ req.member = id ? { id } : null;
10
+ next();
11
+ });
12
+ app.use(bookingsRouter);
13
+
14
+ const port = Number(process.env.PORT ?? 3000);
15
+ app.listen(port, () => console.log(`cabinbook api on :${port}`));
@@ -0,0 +1,23 @@
1
+ import { db } from '../db.js';
2
+ import { isEnabled } from '../flags.js';
3
+ import { quote } from './pricing.js';
4
+
5
+ // Business rule: no stay may be longer than two weeks.
6
+ const MAX_NIGHTS = 14;
7
+ const LONG_STAY_MAX_NIGHTS = 28;
8
+
9
+ export function maxNightsFor(member) {
10
+ if (member && isEnabled('long-stays')) return LONG_STAY_MAX_NIGHTS;
11
+ return MAX_NIGHTS;
12
+ }
13
+
14
+ export async function createBooking({ cabinId, checkIn, nights, guests = 1 }, member) {
15
+ const max = maxNightsFor(member);
16
+ if (nights > max) {
17
+ const err = new Error(`Stays are limited to ${max} nights`);
18
+ err.status = 422;
19
+ throw err;
20
+ }
21
+ const price = quote({ nights, guests });
22
+ return db.insertBooking({ cabinId, checkIn, nights, guests, price, memberId: member?.id ?? null });
23
+ }
@@ -0,0 +1,9 @@
1
+ const NIGHTLY_RATE = 120;
2
+ const EXTRA_GUEST_FEE = 15;
3
+ const WEEKLY_DISCOUNT = 0.1;
4
+
5
+ export function quote({ nights, guests = 1 }) {
6
+ const base = nights * NIGHTLY_RATE + Math.max(0, guests - 2) * EXTRA_GUEST_FEE * nights;
7
+ const discount = nights >= 7 ? base * WEEKLY_DISCOUNT : 0;
8
+ return Math.round((base - discount) * 100) / 100;
9
+ }
@@ -0,0 +1,15 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { quote } from '../src/services/pricing.js';
4
+
5
+ test('charges the nightly rate for short stays', () => {
6
+ assert.equal(quote({ nights: 3 }), 360);
7
+ });
8
+
9
+ test('adds a fee per extra guest per night', () => {
10
+ assert.equal(quote({ nights: 2, guests: 4 }), 300);
11
+ });
12
+
13
+ test('applies the weekly discount from 7 nights', () => {
14
+ assert.equal(quote({ nights: 7 }), 756);
15
+ });
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@cabinbook/web",
3
+ "version": "1.4.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "node --test"
8
+ }
9
+ }
@@ -0,0 +1,11 @@
1
+ // Client-side checks for the booking form, run before POST /bookings.
2
+ export const MAX_NIGHTS = 14;
3
+ export const MAX_GUESTS = 8;
4
+
5
+ export function validateBookingForm({ nights, guests }) {
6
+ const errors = {};
7
+ if (!Number.isInteger(nights) || nights < 1) errors.nights = 'Choose at least one night';
8
+ else if (nights > MAX_NIGHTS) errors.nights = `Stays are limited to ${MAX_NIGHTS} nights`;
9
+ if (!Number.isInteger(guests) || guests < 1 || guests > MAX_GUESTS) errors.guests = `1 to ${MAX_GUESTS} guests`;
10
+ return errors;
11
+ }
@@ -0,0 +1,7 @@
1
+ export function formatPrice(amount) {
2
+ return `€${amount.toFixed(2)}`;
3
+ }
4
+
5
+ export function formatNights(n) {
6
+ return n === 1 ? '1 night' : `${n} nights`;
7
+ }
@@ -0,0 +1,12 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { formatNights, formatPrice } from '../src/format.js';
4
+
5
+ test('formats prices in euros with two decimals', () => {
6
+ assert.equal(formatPrice(756), '€756.00');
7
+ });
8
+
9
+ test('pluralises nights', () => {
10
+ assert.equal(formatNights(1), '1 night');
11
+ assert.equal(formatNights(14), '14 nights');
12
+ });
@@ -0,0 +1,27 @@
1
+ // Generates packages/api/src/generated/schema.js from packages/api/openapi.json.
2
+ // Usage (from the repo root): node scripts/gen-schema.mjs [--check]
3
+ import { readFileSync, writeFileSync } from 'node:fs';
4
+
5
+ const SRC = 'packages/api/openapi.json';
6
+ const DEST = 'packages/api/src/generated/schema.js';
7
+
8
+ const spec = JSON.parse(readFileSync(SRC, 'utf8'));
9
+ const schemas = spec.components.schemas;
10
+
11
+ const out = [
12
+ '// DO NOT EDIT. Generated by scripts/gen-schema.mjs from packages/api/openapi.json.',
13
+ '// Change openapi.json and run `npm run gen`.',
14
+ '',
15
+ `export const schemas = ${JSON.stringify(schemas, null, 2)};`,
16
+ '',
17
+ ].join('\n');
18
+
19
+ if (process.argv.includes('--check')) {
20
+ const current = readFileSync(DEST, 'utf8');
21
+ if (current !== out) {
22
+ console.error(`${DEST} is out of date. Run \`npm run gen\`.`);
23
+ process.exit(1);
24
+ }
25
+ } else {
26
+ writeFileSync(DEST, out);
27
+ }
@@ -0,0 +1,158 @@
1
+ # Worked example: tallyhall, before adding a refund window
2
+
3
+ A fictional FastAPI ticketing service. The README is two releases stale, one rule has a second copy, and one module is generated.
4
+
5
+ ## Input given to the skill
6
+
7
+ > I've just joined and my first ticket is "refunds must be requested within 14 days of the event ending". I've never opened this repo. Get me oriented. The repo is the current directory.
8
+
9
+ The files that mattered, with the line numbers the tour cites:
10
+
11
+ ```text
12
+ README.md
13
+ 5 ## Run it
14
+ 7 make dev
15
+ 12 Settings live in `settings.toml`.
16
+
17
+ Makefile
18
+ 1 .PHONY: run test lint
19
+ 3 run:
20
+ 4 uvicorn tallyhall.main:app --reload
21
+ 7 test:
22
+ 8 pytest -q -m "not e2e"
23
+ 10 lint:
24
+ 11 ruff check tallyhall tests
25
+
26
+ .github/workflows/ci.yml
27
+ 17 - run: pip install -e ".[dev]"
28
+ 19 - run: make lint
29
+ 21 - run: make test
30
+ 23 env:
31
+ 24 TALLYHALL_DB_URL: sqlite:///ci.db
32
+
33
+ pyproject.toml
34
+ 14 [tool.pytest.ini_options]
35
+ 15 markers = ["e2e: needs the payment sandbox"]
36
+
37
+ tallyhall/main.py
38
+ 6 app = FastAPI()
39
+ 7 app.include_router(refunds.router)
40
+
41
+ tallyhall/api/refunds.py
42
+ 18 @router.post("/orders/{order_id}/refunds", status_code=201)
43
+ 19 def create_refund(order_id: int, db=Depends(get_db)):
44
+ 20 order = orders.get(db, order_id)
45
+ 22 check_refundable(order, now=utcnow())
46
+ 24 except RefundRejected as e:
47
+ 25 raise HTTPException(422, {"code": e.code, "message": str(e)})
48
+ 27 return refunds.create(db, order)
49
+
50
+ tallyhall/rules/refunds.py
51
+ 4 class RefundRejected(Exception):
52
+ 9 def check_refundable(order, now):
53
+ 10 if order.status != "paid":
54
+ 11 raise RefundRejected("not_paid", "only paid orders can be refunded")
55
+ 12 if order.refunded_at is not None:
56
+ 13 raise RefundRejected("already_refunded", "order already refunded")
57
+
58
+ tallyhall/admin/bulk_refund.py
59
+ 27 if order.status == "paid" and order.refunded_at is None:
60
+ 28 refunds.create(db, order)
61
+
62
+ tallyhall/models_gen.py
63
+ 1 # generated by datamodel-codegen:
64
+ 2 # filename: schemas/order.json
65
+
66
+ tallyhall/settings.py
67
+ 6 DB_URL = os.environ["TALLYHALL_DB_URL"]
68
+
69
+ tests/rules/test_refunds.py (test_rejects_unpaid, test_rejects_already_refunded)
70
+ tests/api/test_refunds_e2e.py (marked e2e)
71
+ ```
72
+
73
+ The fact lister also reported: `make dev` named at `README.md:7` with no `dev` target in the Makefile, and `settings.toml` named at `README.md:12` and not present. No test file imports `tallyhall/admin/bulk_refund.py`. The Order model (`tallyhall/models_gen.py`) has an `event_ends_at` field.
74
+
75
+ ---
76
+
77
+ ## Output
78
+
79
+ **What matters for this change**
80
+
81
+ - The rule goes in `check_refundable` (`tallyhall/rules/refunds.py:9`). Every other refund rule lives there, and the API calls it at `tallyhall/api/refunds.py:22`.
82
+ - There is a second copy of the refund rules in the support bulk-refund tool (`tallyhall/admin/bulk_refund.py:27`). It doesn't call `check_refundable`, so it won't pick up the window unless someone changes it. Whether it should is the first question for a maintainer.
83
+ - Ignore the README's `make dev` and `settings.toml`. Neither exists. Run with `make run` (`Makefile:3`); settings come from env vars (`tallyhall/settings.py:6`).
84
+ - `tallyhall/models_gen.py` is generated from `schemas/order.json`. Don't edit it. The field the window needs, `event_ends_at`, is already there.
85
+ - `make test` skips the e2e tests (`Makefile:8`, marker at `pyproject.toml:15`). The rule tests in `tests/rules/` do run.
86
+
87
+ # Tour: tallyhall, for "refunds within 14 days of the event ending"
88
+
89
+ _Read on 2026-10-20. Scope: `POST /orders/{order_id}/refunds` from the app bootstrap to `refunds.create`. Skipped on purpose: ticket purchase, the payment-provider client, deployment config._
90
+
91
+ [assumption] "Within 14 days of the event ending" means `now - order.event_ends_at <= 14 days`, with the boundary day allowed.
92
+
93
+ ## Run it
94
+
95
+ | To | Command | Source | Status |
96
+ |---|---|---|---|
97
+ | Install | `pip install -e ".[dev]"` | `.github/workflows/ci.yml:17` | read, not run |
98
+ | Test (whole suite) | `make test` → `pytest -q -m "not e2e"` | `.github/workflows/ci.yml:21`, `Makefile:8` | ran: passed (14 passed, 3 deselected) |
99
+ | Test (this area) | `pytest -q -m "not e2e" tests/rules/test_refunds.py` | derived from `Makefile:8` | ran: passed (2 passed) |
100
+ | Lint | `make lint` → `ruff check tallyhall tests` | `.github/workflows/ci.yml:19`, `Makefile:11` | ran: passed |
101
+ | Run locally | `make run` | `Makefile:3` | read, not run (needs `TALLYHALL_DB_URL`) |
102
+
103
+ Needs: `TALLYHALL_DB_URL`, read at `tallyhall/settings.py:6` with no default; CI sets `sqlite:///ci.db` at `.github/workflows/ci.yml:24`.
104
+
105
+ ## The path this change touches
106
+
107
+ 1. `tallyhall/main.py:7`: the refunds router is mounted on the app.
108
+ 2. `tallyhall/api/refunds.py:19` `create_refund`: loads the order (line 20).
109
+ 3. `tallyhall/api/refunds.py:22`: calls `check_refundable(order, now=utcnow())`.
110
+ 4. `tallyhall/rules/refunds.py:9` `check_refundable`: raises `RefundRejected` for an unpaid order (line 10) or one already refunded (line 12).
111
+ 5. `tallyhall/api/refunds.py:25`: `RefundRejected` becomes a 422 with `{code, message}`.
112
+ 6. `tallyhall/api/refunds.py:27`: `refunds.create` writes the refund. This is the far edge.
113
+
114
+ ## Where the change goes
115
+
116
+ - `tallyhall/rules/refunds.py` `check_refundable`: add the window check after line 13, raising `RefundRejected("refund_window_closed", …)`. This follows the two rules already there.
117
+ - Must change with it: nothing generated. `event_ends_at` already exists in `tallyhall/models_gen.py`. Whether `tallyhall/admin/bulk_refund.py:27` changes too is open (question 1).
118
+
119
+ ## Conventions the code follows
120
+
121
+ - **Errors:** a rule raises `RefundRejected(code, message)` with a snake_case code (`tallyhall/rules/refunds.py:11`); the route maps it to 422 (`tallyhall/api/refunds.py:25`).
122
+ - **Time:** the route passes `now` in, so rules are tested without a clock (`tallyhall/api/refunds.py:22`).
123
+ - **Tests:** one `test_rejects_<reason>` per rule, under `tests/rules/` (`tests/rules/test_refunds.py`).
124
+ - **Config:** env vars read in `tallyhall/settings.py`; a missing var fails at import (`tallyhall/settings.py:6`).
125
+
126
+ ## Traps
127
+
128
+ - **Generated:** `tallyhall/models_gen.py` from `schemas/order.json` (`tallyhall/models_gen.py:1-2`). No regenerate command is defined in the Makefile, pyproject or CI; see question 2. CI drift check: none found.
129
+ - **Second copy:** `tallyhall/admin/bulk_refund.py:27` repeats the paid and not-refunded checks inline.
130
+ - **Untested on the path:** none on the API path. Off the path, `tallyhall/admin/bulk_refund.py` has no test.
131
+ - **Tests the default command skips:** `e2e` (`Makefile:8`, `pyproject.toml:15`): `tests/api/test_refunds_e2e.py`, which needs the payment sandbox.
132
+ - **Env vars and flags on the path:** `TALLYHALL_DB_URL` only. No feature flags found.
133
+ - **Vendored:** none.
134
+
135
+ ## Where the README is wrong
136
+
137
+ - README says `make dev` (`README.md:7`); the Makefile has `run`, `test` and `lint` only (`Makefile:1`). Use `make run`.
138
+ - README says settings live in `settings.toml` (`README.md:12`); there is no such file, and settings are env vars (`tallyhall/settings.py:6`).
139
+
140
+ ## Tests for this area
141
+
142
+ - Covered by: `tests/rules/test_refunds.py` (unpaid and already-refunded rejections).
143
+ - Run just these: `pytest -q -m "not e2e" tests/rules/test_refunds.py` (derived from `Makefile:8`).
144
+ - Not covered: the 422 mapping is exercised only by the e2e test, which `make test` skips.
145
+
146
+ ## First step
147
+
148
+ Add `test_rejects_refund_after_window` to `tests/rules/test_refunds.py`, with an order whose `event_ends_at` is 15 days before `now`. Run `pytest -q -m "not e2e" tests/rules/test_refunds.py`. It fails until `check_refundable` checks the window. Add a 14-day case that passes, to pin the boundary.
149
+
150
+ ## Open questions for a maintainer
151
+
152
+ 1. Should the bulk-refund tool (`tallyhall/admin/bulk_refund.py:27`) apply the window, or is it the support override that skips it?
153
+ 2. How is `tallyhall/models_gen.py` regenerated? Nothing in the repo defines the command.
154
+ 3. Is the window measured from `event_ends_at` in UTC? `utcnow()` is used at `tallyhall/api/refunds.py:22`, but the schema doesn't say what timezone the event times are in.
155
+
156
+ ---
157
+
158
+ Why this passes the bar: every command has a source and a status, both README claims are contradicted with citations, the second copy is named and left as a question rather than silently changed or ignored, the generated file is called out before anyone edits it, and the first step names a file, a command and the failure that proves it.
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "codebase-tour",
3
+ "version": "0.1.0",
4
+ "description": "Get a newcomer (a new engineer, or the agent itself) oriented in an unfamiliar repository before they make one specific change, with a short tour scoped to that change. It gives the install, run and test commands taken from CI, package scripts and the Makefile; the path the change touches, traced through the code with file:line citations; where the change goes; the conventions the code actually follows; the traps (generated files, a second copy of the logic, untested code, env vars, flags); and a first step that can be checked. Use when someone says \"I'm new to this repo\", \"get me oriented\", \"where would I make this change\", \"walk me through the code for X\", \"onboard me before I start this ticket\", or \"how do I run and test this\".",
5
+ "entrypoint": "SKILL.md",
6
+ "deps": {
7
+ "npm": [],
8
+ "pip": []
9
+ },
10
+ "env": {
11
+ "required": [],
12
+ "optional": []
13
+ },
14
+ "related": [
15
+ "agents-md-generator",
16
+ "architecture-diagrams",
17
+ "feature-implement-loop",
18
+ "interactive-diagram",
19
+ "pre-merge-review",
20
+ "reverse-architecture"
21
+ ],
22
+ "handoff": [
23
+ {
24
+ "to": "feature-implement-loop",
25
+ "when": "the tour names where the change goes and a first step",
26
+ "purpose": "Implement the change against acceptance criteria, starting from the tour's first step and running the area's test command it found.",
27
+ "fallback": "If feature-implement-loop is absent, take the tour's first step: write the failing test it names, run the area's test command before and after the change, and update every place listed under 'Must change with it'."
28
+ }
29
+ ],
30
+ "tags": [
31
+ "onboarding",
32
+ "codebase",
33
+ "repo-orientation",
34
+ "provenance",
35
+ "developer-experience"
36
+ ],
37
+ "permissions": {
38
+ "network": [],
39
+ "commands": [],
40
+ "files": "none",
41
+ "notes": "scripts/repo_facts.py reads files under the repository root it is given and prints to stdout. It writes nothing and runs no commands."
42
+ },
43
+ "model": {
44
+ "tier": "standard",
45
+ "rationale": "Reading a repo and citing what it finds is generation over evidence; every claim is checked against a file, so a mid tier is enough."
46
+ }
47
+ }