agentic-loop 1.0.0

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 (162) hide show
  1. package/.claude/commands/explain.md +114 -0
  2. package/.claude/commands/idea.md +398 -0
  3. package/.claude/commands/my-dna.md +122 -0
  4. package/.claude/commands/prd.md +286 -0
  5. package/.claude/commands/review.md +167 -0
  6. package/.claude/commands/sign.md +32 -0
  7. package/.claude/commands/styleguide.md +450 -0
  8. package/.claude/commands/tour.md +301 -0
  9. package/.claude/commands/vibe-check.md +116 -0
  10. package/.claude/commands/vibe-help.md +47 -0
  11. package/.claude/commands/vibe-list.md +203 -0
  12. package/.pre-commit-hooks.yaml +102 -0
  13. package/LICENSE +21 -0
  14. package/README.md +238 -0
  15. package/bin/agentic-loop.sh +24 -0
  16. package/bin/postinstall.sh +29 -0
  17. package/bin/ralph.sh +171 -0
  18. package/bin/vibe-check.js +19 -0
  19. package/dist/checks/check-any-types.d.ts +6 -0
  20. package/dist/checks/check-any-types.d.ts.map +1 -0
  21. package/dist/checks/check-any-types.js +73 -0
  22. package/dist/checks/check-any-types.js.map +1 -0
  23. package/dist/checks/check-commented-code.d.ts +6 -0
  24. package/dist/checks/check-commented-code.d.ts.map +1 -0
  25. package/dist/checks/check-commented-code.js +81 -0
  26. package/dist/checks/check-commented-code.js.map +1 -0
  27. package/dist/checks/check-console-error.d.ts +6 -0
  28. package/dist/checks/check-console-error.d.ts.map +1 -0
  29. package/dist/checks/check-console-error.js +41 -0
  30. package/dist/checks/check-console-error.js.map +1 -0
  31. package/dist/checks/check-debug-statements.d.ts +6 -0
  32. package/dist/checks/check-debug-statements.d.ts.map +1 -0
  33. package/dist/checks/check-debug-statements.js +120 -0
  34. package/dist/checks/check-debug-statements.js.map +1 -0
  35. package/dist/checks/check-deep-nesting.d.ts +6 -0
  36. package/dist/checks/check-deep-nesting.d.ts.map +1 -0
  37. package/dist/checks/check-deep-nesting.js +116 -0
  38. package/dist/checks/check-deep-nesting.js.map +1 -0
  39. package/dist/checks/check-docker-platform.d.ts +6 -0
  40. package/dist/checks/check-docker-platform.d.ts.map +1 -0
  41. package/dist/checks/check-docker-platform.js +42 -0
  42. package/dist/checks/check-docker-platform.js.map +1 -0
  43. package/dist/checks/check-dry-violations.d.ts +6 -0
  44. package/dist/checks/check-dry-violations.d.ts.map +1 -0
  45. package/dist/checks/check-dry-violations.js +124 -0
  46. package/dist/checks/check-dry-violations.js.map +1 -0
  47. package/dist/checks/check-empty-catch.d.ts +6 -0
  48. package/dist/checks/check-empty-catch.d.ts.map +1 -0
  49. package/dist/checks/check-empty-catch.js +111 -0
  50. package/dist/checks/check-empty-catch.js.map +1 -0
  51. package/dist/checks/check-function-length.d.ts +6 -0
  52. package/dist/checks/check-function-length.d.ts.map +1 -0
  53. package/dist/checks/check-function-length.js +152 -0
  54. package/dist/checks/check-function-length.js.map +1 -0
  55. package/dist/checks/check-hardcoded-ai-models.d.ts +10 -0
  56. package/dist/checks/check-hardcoded-ai-models.d.ts.map +1 -0
  57. package/dist/checks/check-hardcoded-ai-models.js +102 -0
  58. package/dist/checks/check-hardcoded-ai-models.js.map +1 -0
  59. package/dist/checks/check-hardcoded-urls.d.ts +6 -0
  60. package/dist/checks/check-hardcoded-urls.d.ts.map +1 -0
  61. package/dist/checks/check-hardcoded-urls.js +124 -0
  62. package/dist/checks/check-hardcoded-urls.js.map +1 -0
  63. package/dist/checks/check-magic-numbers.d.ts +6 -0
  64. package/dist/checks/check-magic-numbers.d.ts.map +1 -0
  65. package/dist/checks/check-magic-numbers.js +116 -0
  66. package/dist/checks/check-magic-numbers.js.map +1 -0
  67. package/dist/checks/check-secrets.d.ts +6 -0
  68. package/dist/checks/check-secrets.d.ts.map +1 -0
  69. package/dist/checks/check-secrets.js +138 -0
  70. package/dist/checks/check-secrets.js.map +1 -0
  71. package/dist/checks/check-snake-case-ts.d.ts +6 -0
  72. package/dist/checks/check-snake-case-ts.d.ts.map +1 -0
  73. package/dist/checks/check-snake-case-ts.js +78 -0
  74. package/dist/checks/check-snake-case-ts.js.map +1 -0
  75. package/dist/checks/check-todo-fixme.d.ts +6 -0
  76. package/dist/checks/check-todo-fixme.d.ts.map +1 -0
  77. package/dist/checks/check-todo-fixme.js +41 -0
  78. package/dist/checks/check-todo-fixme.js.map +1 -0
  79. package/dist/checks/check-unsafe-html.d.ts +6 -0
  80. package/dist/checks/check-unsafe-html.d.ts.map +1 -0
  81. package/dist/checks/check-unsafe-html.js +101 -0
  82. package/dist/checks/check-unsafe-html.js.map +1 -0
  83. package/dist/checks/index.d.ts +30 -0
  84. package/dist/checks/index.d.ts.map +1 -0
  85. package/dist/checks/index.js +57 -0
  86. package/dist/checks/index.js.map +1 -0
  87. package/dist/cli.d.ts +13 -0
  88. package/dist/cli.d.ts.map +1 -0
  89. package/dist/cli.js +208 -0
  90. package/dist/cli.js.map +1 -0
  91. package/dist/index.d.ts +9 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +10 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/utils/file-reader.d.ts +24 -0
  96. package/dist/utils/file-reader.d.ts.map +1 -0
  97. package/dist/utils/file-reader.js +146 -0
  98. package/dist/utils/file-reader.js.map +1 -0
  99. package/dist/utils/patterns.d.ts +27 -0
  100. package/dist/utils/patterns.d.ts.map +1 -0
  101. package/dist/utils/patterns.js +84 -0
  102. package/dist/utils/patterns.js.map +1 -0
  103. package/dist/utils/reporters.d.ts +21 -0
  104. package/dist/utils/reporters.d.ts.map +1 -0
  105. package/dist/utils/reporters.js +115 -0
  106. package/dist/utils/reporters.js.map +1 -0
  107. package/dist/utils/types.d.ts +71 -0
  108. package/dist/utils/types.d.ts.map +1 -0
  109. package/dist/utils/types.js +5 -0
  110. package/dist/utils/types.js.map +1 -0
  111. package/package.json +83 -0
  112. package/ralph/api.sh +216 -0
  113. package/ralph/backup.sh +838 -0
  114. package/ralph/browser-verify/README.md +135 -0
  115. package/ralph/browser-verify/verify.ts +450 -0
  116. package/ralph/checks/check-fastapi-responses.py +155 -0
  117. package/ralph/hooks/hooks-config.json +72 -0
  118. package/ralph/hooks/inject-context.sh +44 -0
  119. package/ralph/hooks/install.sh +207 -0
  120. package/ralph/hooks/log-tools.sh +45 -0
  121. package/ralph/hooks/protect-prd.sh +27 -0
  122. package/ralph/hooks/save-learnings.sh +36 -0
  123. package/ralph/hooks/warn-debug.sh +54 -0
  124. package/ralph/hooks/warn-empty-catch.sh +63 -0
  125. package/ralph/hooks/warn-secrets.sh +89 -0
  126. package/ralph/hooks/warn-urls.sh +77 -0
  127. package/ralph/init.sh +515 -0
  128. package/ralph/loop.sh +730 -0
  129. package/ralph/playwright.sh +238 -0
  130. package/ralph/prd.sh +295 -0
  131. package/ralph/setup/feature-tour.sh +155 -0
  132. package/ralph/setup/quick-setup.sh +239 -0
  133. package/ralph/setup/tutorial.sh +159 -0
  134. package/ralph/setup/ui.sh +136 -0
  135. package/ralph/setup.sh +401 -0
  136. package/ralph/signs.sh +150 -0
  137. package/ralph/utils.sh +682 -0
  138. package/ralph/verify/browser.sh +324 -0
  139. package/ralph/verify/lint.sh +363 -0
  140. package/ralph/verify/review.sh +152 -0
  141. package/ralph/verify/tests.sh +81 -0
  142. package/ralph/verify.sh +268 -0
  143. package/templates/PROMPT.md +235 -0
  144. package/templates/config/fullstack.json +86 -0
  145. package/templates/config/go.json +81 -0
  146. package/templates/config/minimal.json +76 -0
  147. package/templates/config/node.json +81 -0
  148. package/templates/config/python.json +81 -0
  149. package/templates/config/rust.json +81 -0
  150. package/templates/examples/CLAUDE-django.md +174 -0
  151. package/templates/examples/CLAUDE-fastapi.md +270 -0
  152. package/templates/examples/CLAUDE-fastmcp.md +352 -0
  153. package/templates/examples/CLAUDE-fullstack.md +256 -0
  154. package/templates/examples/CLAUDE-node.md +246 -0
  155. package/templates/examples/CLAUDE-react.md +138 -0
  156. package/templates/optional/cursorrules.template +147 -0
  157. package/templates/optional/eslint.config.js +34 -0
  158. package/templates/optional/lint-staged.config.js +34 -0
  159. package/templates/optional/ruff.toml +125 -0
  160. package/templates/optional/vibe-check.yml +116 -0
  161. package/templates/optional/vscode-settings.json +127 -0
  162. package/templates/signs.json +46 -0
@@ -0,0 +1,125 @@
1
+ # Ruff Configuration for Vibe Coding
2
+ #
3
+ # Catches common AI-generated code issues in Python.
4
+ # Copy to your project root as ruff.toml or pyproject.toml [tool.ruff] section.
5
+ #
6
+ # Install: pip install ruff
7
+
8
+ # Target Python version
9
+ target-version = "py311"
10
+
11
+ # Line length (match Black)
12
+ line-length = 100
13
+
14
+ # Exclude common directories
15
+ exclude = [
16
+ ".git",
17
+ ".venv",
18
+ "venv",
19
+ "__pycache__",
20
+ "migrations",
21
+ "node_modules",
22
+ "dist",
23
+ "build",
24
+ ".eggs",
25
+ ]
26
+
27
+ [lint]
28
+ # Enable these rule sets
29
+ select = [
30
+ # ===========================================
31
+ # Core rules - catch common AI mistakes
32
+ # ===========================================
33
+ "E", # pycodestyle errors
34
+ "W", # pycodestyle warnings
35
+ "F", # Pyflakes (unused imports, variables)
36
+ "I", # isort (import sorting)
37
+ "B", # flake8-bugbear (common bugs)
38
+ "C4", # flake8-comprehensions
39
+ "UP", # pyupgrade (modern Python syntax)
40
+ "SIM", # flake8-simplify (simplify code)
41
+
42
+ # ===========================================
43
+ # Security - AI can introduce vulnerabilities
44
+ # ===========================================
45
+ "S", # flake8-bandit (security issues)
46
+
47
+ # ===========================================
48
+ # Code quality
49
+ # ===========================================
50
+ "N", # pep8-naming
51
+ "RUF", # Ruff-specific rules
52
+ "PTH", # flake8-use-pathlib
53
+ "ERA", # eradicate (commented-out code)
54
+ "PL", # Pylint
55
+ "PERF", # Perflint (performance)
56
+
57
+ # ===========================================
58
+ # Type checking helpers
59
+ # ===========================================
60
+ "TCH", # flake8-type-checking
61
+ "ANN", # flake8-annotations
62
+ ]
63
+
64
+ # Rules to ignore
65
+ ignore = [
66
+ # Allow missing type annotations in some cases
67
+ "ANN101", # Missing type annotation for self
68
+ "ANN102", # Missing type annotation for cls
69
+ "ANN401", # Dynamically typed expressions (Any)
70
+
71
+ # Allow assert in tests
72
+ "S101", # Use of assert
73
+
74
+ # These can be too strict
75
+ "PLR0913", # Too many arguments
76
+ "PLR2004", # Magic value comparison
77
+ "PLR0911", # Too many return statements
78
+
79
+ # Let formatter handle these
80
+ "E501", # Line too long (handled by formatter)
81
+ ]
82
+
83
+ # Per-file ignores
84
+ [lint.per-file-ignores]
85
+ # Tests can have more relaxed rules
86
+ "tests/**/*.py" = [
87
+ "S101", # assert is fine in tests
88
+ "ANN", # Don't require annotations in tests
89
+ "PLR2004", # Magic values OK in tests
90
+ ]
91
+ # Migrations are auto-generated
92
+ "**/migrations/*.py" = ["ALL"]
93
+ # Config files
94
+ "conftest.py" = ["ANN"]
95
+
96
+ [lint.isort]
97
+ # isort configuration
98
+ known-first-party = ["app", "core", "services"]
99
+ force-single-line = false
100
+ combine-as-imports = true
101
+
102
+ [lint.flake8-bugbear]
103
+ # Extend immutable calls (AI often mutates defaults)
104
+ extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]
105
+
106
+ [lint.pylint]
107
+ # Limit function complexity
108
+ max-args = 7
109
+ max-branches = 12
110
+ max-returns = 6
111
+ max-statements = 50
112
+
113
+ [lint.mccabe]
114
+ # Cyclomatic complexity threshold
115
+ max-complexity = 10
116
+
117
+ [format]
118
+ # Use double quotes (match Black)
119
+ quote-style = "double"
120
+
121
+ # Indent with spaces
122
+ indent-style = "space"
123
+
124
+ # Unix line endings
125
+ line-ending = "lf"
@@ -0,0 +1,116 @@
1
+ # GitHub Action: Vibe Check
2
+ # Runs vibe-and-thrive pre-commit hooks on PRs
3
+ #
4
+ # Add this to your repo: .github/workflows/vibe-check.yml
5
+
6
+ name: Vibe Check
7
+
8
+ on:
9
+ pull_request:
10
+ branches: [main, master]
11
+ push:
12
+ branches: [main, master]
13
+
14
+ jobs:
15
+ vibe-check:
16
+ name: Code Quality Check
17
+ runs-on: ubuntu-latest
18
+
19
+ steps:
20
+ - name: Checkout code
21
+ uses: actions/checkout@v4
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: '3.11'
27
+
28
+ - name: Set up Node.js
29
+ uses: actions/setup-node@v4
30
+ with:
31
+ node-version: '20'
32
+
33
+ - name: Install pre-commit
34
+ run: pip install pre-commit
35
+
36
+ - name: Run pre-commit hooks
37
+ run: pre-commit run --all-files --show-diff-on-failure
38
+ continue-on-error: true
39
+ id: precommit
40
+
41
+ - name: Comment on PR (if issues found)
42
+ if: steps.precommit.outcome == 'failure' && github.event_name == 'pull_request'
43
+ uses: actions/github-script@v7
44
+ with:
45
+ script: |
46
+ github.rest.issues.createComment({
47
+ issue_number: context.issue.number,
48
+ owner: context.repo.owner,
49
+ repo: context.repo.repo,
50
+ body: `## ⚠️ Vibe Check Found Issues
51
+
52
+ The pre-commit hooks found some issues in this PR. Please review the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.
53
+
54
+ Common fixes:
55
+ - \`any\` types → Create proper TypeScript interfaces
56
+ - Empty catch blocks → Add proper error handling
57
+ - Hardcoded URLs → Use environment variables
58
+ - console.log → Remove or use proper logging
59
+
60
+ Run \`pre-commit run --all-files\` locally to see all issues.`
61
+ })
62
+
63
+ - name: Check for blocking issues
64
+ run: |
65
+ # Only fail on blocking hooks (secrets, hardcoded URLs)
66
+ pre-commit run check-secrets --all-files
67
+ pre-commit run check-hardcoded-urls --all-files
68
+
69
+ # Optional: TypeScript type checking
70
+ typecheck:
71
+ name: TypeScript Check
72
+ runs-on: ubuntu-latest
73
+ if: hashFiles('**/tsconfig.json') != ''
74
+
75
+ steps:
76
+ - name: Checkout code
77
+ uses: actions/checkout@v4
78
+
79
+ - name: Set up Node.js
80
+ uses: actions/setup-node@v4
81
+ with:
82
+ node-version: '20'
83
+ cache: 'npm'
84
+
85
+ - name: Install dependencies
86
+ run: npm ci
87
+ working-directory: ${{ hashFiles('frontend/package.json') != '' && 'frontend' || '.' }}
88
+
89
+ - name: Run type check
90
+ run: npm run typecheck || npm run tsc -- --noEmit
91
+ working-directory: ${{ hashFiles('frontend/package.json') != '' && 'frontend' || '.' }}
92
+ continue-on-error: true
93
+
94
+ # Optional: Python type checking
95
+ mypy:
96
+ name: Python Type Check
97
+ runs-on: ubuntu-latest
98
+ if: hashFiles('**/pyproject.toml') != '' || hashFiles('**/setup.py') != ''
99
+
100
+ steps:
101
+ - name: Checkout code
102
+ uses: actions/checkout@v4
103
+
104
+ - name: Set up Python
105
+ uses: actions/setup-python@v5
106
+ with:
107
+ python-version: '3.11'
108
+
109
+ - name: Install dependencies
110
+ run: |
111
+ pip install mypy
112
+ pip install -e . || pip install -r requirements.txt || true
113
+
114
+ - name: Run mypy
115
+ run: mypy . --ignore-missing-imports
116
+ continue-on-error: true
@@ -0,0 +1,127 @@
1
+ {
2
+ // VS Code Settings for Better AI-Assisted Coding
3
+ // Copy relevant settings to your .vscode/settings.json
4
+
5
+ // ============================================
6
+ // TypeScript - Catch issues early
7
+ // ============================================
8
+ "typescript.tsdk": "node_modules/typescript/lib",
9
+ "typescript.enablePromptUseWorkspaceTsdk": true,
10
+ "typescript.preferences.quoteStyle": "single",
11
+ "typescript.suggest.completeFunctionCalls": true,
12
+ "typescript.inlayHints.parameterNames.enabled": "all",
13
+ "typescript.inlayHints.functionLikeReturnTypes.enabled": true,
14
+
15
+ // Strict null checks in editor
16
+ "typescript.strict": true,
17
+
18
+ // ============================================
19
+ // Editor - Clean code formatting
20
+ // ============================================
21
+ "editor.formatOnSave": true,
22
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
23
+ "editor.codeActionsOnSave": {
24
+ "source.fixAll.eslint": "explicit",
25
+ "source.organizeImports": "explicit"
26
+ },
27
+
28
+ // Show problems in editor
29
+ "editor.rulers": [80, 100],
30
+ "editor.wordWrap": "wordWrapColumn",
31
+ "editor.wordWrapColumn": 100,
32
+
33
+ // ============================================
34
+ // ESLint - Enforce code quality
35
+ // ============================================
36
+ "eslint.validate": [
37
+ "javascript",
38
+ "javascriptreact",
39
+ "typescript",
40
+ "typescriptreact"
41
+ ],
42
+ "eslint.codeActionsOnSave.mode": "all",
43
+
44
+ // ============================================
45
+ // Python - Type hints and formatting
46
+ // ============================================
47
+ "[python]": {
48
+ "editor.defaultFormatter": "ms-python.black-formatter",
49
+ "editor.formatOnSave": true,
50
+ "editor.codeActionsOnSave": {
51
+ "source.organizeImports": "explicit"
52
+ }
53
+ },
54
+ "python.analysis.typeCheckingMode": "basic",
55
+ "python.analysis.autoImportCompletions": true,
56
+
57
+ // ============================================
58
+ // Files - Keep workspace clean
59
+ // ============================================
60
+ "files.exclude": {
61
+ "**/__pycache__": true,
62
+ "**/.pytest_cache": true,
63
+ "**/node_modules": true,
64
+ "**/.git": true,
65
+ "**/dist": true,
66
+ "**/build": true,
67
+ "**/*.pyc": true
68
+ },
69
+ "files.trimTrailingWhitespace": true,
70
+ "files.insertFinalNewline": true,
71
+
72
+ // ============================================
73
+ // Search - Focus on relevant files
74
+ // ============================================
75
+ "search.exclude": {
76
+ "**/node_modules": true,
77
+ "**/dist": true,
78
+ "**/build": true,
79
+ "**/.git": true,
80
+ "**/coverage": true,
81
+ "**/*.min.js": true,
82
+ "**/package-lock.json": true,
83
+ "**/yarn.lock": true
84
+ },
85
+
86
+ // ============================================
87
+ // Git - Clean commits
88
+ // ============================================
89
+ "git.enableSmartCommit": true,
90
+ "git.confirmSync": false,
91
+ "git.autofetch": true,
92
+
93
+ // ============================================
94
+ // Terminal - Easy testing
95
+ // ============================================
96
+ "terminal.integrated.defaultProfile.osx": "zsh",
97
+ "terminal.integrated.defaultProfile.linux": "bash",
98
+
99
+ // ============================================
100
+ // Extensions - Recommended for vibe coding
101
+ // ============================================
102
+ // Install these extensions:
103
+ // - ESLint (dbaeumer.vscode-eslint)
104
+ // - Prettier (esbenp.prettier-vscode)
105
+ // - TypeScript Error Translator (mattpocock.ts-error-translator)
106
+ // - Error Lens (usernamehw.errorlens) - Shows errors inline
107
+ // - GitLens (eamodio.gitlens) - Better git integration
108
+ // - Python (ms-python.python)
109
+ // - Black Formatter (ms-python.black-formatter)
110
+ // - Pylance (ms-python.vscode-pylance)
111
+
112
+ // ============================================
113
+ // AI Assistant Settings
114
+ // ============================================
115
+ // If using GitHub Copilot:
116
+ "github.copilot.enable": {
117
+ "*": true,
118
+ "yaml": false,
119
+ "plaintext": false,
120
+ "markdown": false,
121
+ "env": false
122
+ },
123
+
124
+ // Show AI suggestions but don't auto-accept
125
+ "editor.inlineSuggest.enabled": true,
126
+ "editor.suggestSelection": "first"
127
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "signs": [
3
+ {
4
+ "id": "sign-001",
5
+ "pattern": "NEVER hardcode AI model names (gpt-4, claude-3, gemini, etc) - always use environment variables or settings",
6
+ "category": "backend",
7
+ "learnedFrom": null,
8
+ "createdAt": "2026-01-20T00:00:00-08:00"
9
+ },
10
+ {
11
+ "id": "sign-002",
12
+ "pattern": "Use environment variables for all API keys and secrets, never hardcode them",
13
+ "category": "general",
14
+ "learnedFrom": null,
15
+ "createdAt": "2026-01-20T00:00:00-08:00"
16
+ },
17
+ {
18
+ "id": "sign-003",
19
+ "pattern": "Always handle loading, error, and empty states for async operations",
20
+ "category": "frontend",
21
+ "learnedFrom": null,
22
+ "createdAt": "2026-01-20T00:00:00-08:00"
23
+ },
24
+ {
25
+ "id": "sign-004",
26
+ "pattern": "Use camelCase for TypeScript/JavaScript variables, functions, and API response fields - convert snake_case from backend to camelCase in frontend",
27
+ "category": "frontend",
28
+ "learnedFrom": null,
29
+ "createdAt": "2026-01-20T00:00:00-08:00"
30
+ },
31
+ {
32
+ "id": "sign-005",
33
+ "pattern": "Add data-testid attributes to interactive elements for Playwright e2e tests",
34
+ "category": "frontend",
35
+ "learnedFrom": null,
36
+ "createdAt": "2026-01-20T00:00:00-08:00"
37
+ },
38
+ {
39
+ "id": "sign-006",
40
+ "pattern": "Never use 'any' type in TypeScript - create proper interfaces and types instead",
41
+ "category": "frontend",
42
+ "learnedFrom": null,
43
+ "createdAt": "2026-01-20T00:00:00-08:00"
44
+ }
45
+ ]
46
+ }