ai-devcontext 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.
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/package.json +42 -0
- package/src/index.js +90 -0
- package/templates/.claude/agents/security-scanner/security-scanner.md +27 -0
- package/templates/.claude/commands/clean.sh +6 -0
- package/templates/.claude/commands/db-migrate.sh +6 -0
- package/templates/.claude/commands/docker-down.sh +5 -0
- package/templates/.claude/commands/docker-up.sh +11 -0
- package/templates/.claude/commands/format.sh +9 -0
- package/templates/.claude/commands/lint-fix.sh +5 -0
- package/templates/.claude/commands/new-resource.sh +76 -0
- package/templates/.claude/commands/outdated-deps.sh +7 -0
- package/templates/.claude/commands/seed-db.sh +5 -0
- package/templates/.claude/commands/test-watch.sh +5 -0
- package/templates/.claude/commands/typecheck.sh +9 -0
- package/templates/.claude/hooks/after-edit.sh +12 -0
- package/templates/.claude/rules/architectural-docs.md +7 -0
- package/templates/.claude/rules/security-checklist.md +25 -0
- package/templates/.claude/settings.json +15 -0
- package/templates/.claude/settings.local.json +5 -0
- package/templates/.claude/skills/testing-patterns/SKILL.md +22 -0
- package/templates/.claude/workflows/deployment-guide.md +13 -0
- package/templates/.cursor/.cursorignore +12 -0
- package/templates/.cursor/.cursorrules +24 -0
- package/templates/.cursor/commands/build-and-test.sh +8 -0
- package/templates/.cursor/commands/clean.sh +6 -0
- package/templates/.cursor/commands/docker-down.sh +5 -0
- package/templates/.cursor/commands/docker-up.sh +11 -0
- package/templates/.cursor/commands/format.sh +9 -0
- package/templates/.cursor/commands/lint-fix.sh +5 -0
- package/templates/.cursor/commands/new-resource.sh +76 -0
- package/templates/.cursor/commands/outdated-deps.sh +7 -0
- package/templates/.cursor/commands/seed-db.sh +5 -0
- package/templates/.cursor/commands/test-watch.sh +5 -0
- package/templates/.cursor/commands/typecheck.sh +9 -0
- package/templates/.cursor/cursor.json +9 -0
- package/templates/.cursor/hooks/after-edit.sh +13 -0
- package/templates/.cursor/rules/backend-rules.mdc +19 -0
- package/templates/.cursor/rules/ui-conventions.md +8 -0
- package/templates/.cursor/skills/code-reviewer/SKILL.md +16 -0
- package/templates/CLAUDE.md +46 -0
- package/templates/claude.local.md +11 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abu Hurayra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# ai-devcontext
|
|
2
|
+
|
|
3
|
+
Instantly bootstrap AI coding assistant configuration into any project — `CLAUDE.md`, `claude.local.md`, `.claude/` (rules, skills, commands, agents, hooks, workflows), and `.cursor/` (rules, commands, skills, hooks).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g ai-devcontext
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or run it once without installing:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx ai-devcontext init
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
From the root of any project:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
devcontext init
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This copies the full template tree into your project, skipping any file that already exists, marks shell command scripts executable, and adds `claude.local.md` / `.claude/settings.local.json` to your `.gitignore`.
|
|
26
|
+
|
|
27
|
+
## What gets created
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
CLAUDE.md Root project guide (stack, build/test/lint, conventions)
|
|
31
|
+
claude.local.md Personal, git-ignored local notes
|
|
32
|
+
|
|
33
|
+
.cursor/
|
|
34
|
+
├── .cursorignore
|
|
35
|
+
├── cursor.json
|
|
36
|
+
├── .cursorrules Legacy fallback rules
|
|
37
|
+
├── rules/ Scoped rules (backend, UI conventions)
|
|
38
|
+
├── commands/ Dev task scripts (lint, format, test, docker, db, etc.)
|
|
39
|
+
├── skills/code-reviewer/
|
|
40
|
+
└── hooks/after-edit.sh
|
|
41
|
+
|
|
42
|
+
.claude/
|
|
43
|
+
├── settings.json
|
|
44
|
+
├── settings.local.json Machine-specific, git-ignored
|
|
45
|
+
├── rules/ Architecture + security checklist
|
|
46
|
+
├── skills/testing-patterns/
|
|
47
|
+
├── commands/ Same dev task scripts as .cursor/commands/
|
|
48
|
+
├── agents/security-scanner/
|
|
49
|
+
├── hooks/after-edit.sh
|
|
50
|
+
└── workflows/deployment-guide.md
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-devcontext",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Instantly bootstrap AI configuration templates (CLAUDE.md, .claude/, .cursor/) into any project.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"devcontext": "./src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"src",
|
|
11
|
+
"templates"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node src/index.js"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"chalk": "^5.3.0",
|
|
18
|
+
"commander": "^12.0.0",
|
|
19
|
+
"fs-extra": "^11.2.0"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"ai",
|
|
26
|
+
"cursor",
|
|
27
|
+
"claude",
|
|
28
|
+
"devtools",
|
|
29
|
+
"cli",
|
|
30
|
+
"templates"
|
|
31
|
+
],
|
|
32
|
+
"author": "Abu Hurayra <abu.hurayra@sjinnovation.com>",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/abuhurayra0889/ai-devcontext-cli.git"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/abuhurayra0889/ai-devcontext-cli/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/abuhurayra0889/ai-devcontext-cli#readme"
|
|
42
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
|
|
9
|
+
// ESM __dirname fix
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
|
|
13
|
+
// Files that should never be committed by the consuming project.
|
|
14
|
+
const GITIGNORE_ENTRIES = ['claude.local.md', '.claude/settings.local.json'];
|
|
15
|
+
|
|
16
|
+
const program = new Command();
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.name('devcontext')
|
|
20
|
+
.description('Bootstrap AI configuration templates into your project.')
|
|
21
|
+
.version('1.0.0');
|
|
22
|
+
|
|
23
|
+
async function copyTemplateTree(srcDir, destDir, targetDir) {
|
|
24
|
+
const entries = await fs.readdir(srcDir, { withFileTypes: true });
|
|
25
|
+
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
28
|
+
const destPath = path.join(destDir, entry.name);
|
|
29
|
+
const relPath = path.relative(targetDir, destPath);
|
|
30
|
+
|
|
31
|
+
if (entry.isDirectory()) {
|
|
32
|
+
await copyTemplateTree(srcPath, destPath, targetDir);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const exists = await fs.pathExists(destPath);
|
|
37
|
+
if (exists) {
|
|
38
|
+
console.log(chalk.yellow(` ⚠ Skipped → ${relPath} (already exists)`));
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
43
|
+
await fs.copy(srcPath, destPath);
|
|
44
|
+
|
|
45
|
+
if (entry.name.endsWith('.sh')) {
|
|
46
|
+
await fs.chmod(destPath, 0o755);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(chalk.green(` ✔ Created → ${relPath}`));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function updateGitignore(targetDir) {
|
|
54
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
55
|
+
const existing = (await fs.pathExists(gitignorePath))
|
|
56
|
+
? await fs.readFile(gitignorePath, 'utf8')
|
|
57
|
+
: '';
|
|
58
|
+
|
|
59
|
+
const missing = GITIGNORE_ENTRIES.filter(
|
|
60
|
+
(entry) => !existing.split('\n').some((line) => line.trim() === entry)
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (missing.length === 0) return;
|
|
64
|
+
|
|
65
|
+
const header = existing.trim().length > 0 ? '\n\n# devcontext: local/personal AI context\n' : '# devcontext: local/personal AI context\n';
|
|
66
|
+
await fs.appendFile(gitignorePath, header + missing.join('\n') + '\n');
|
|
67
|
+
console.log(chalk.green(` ✔ Updated → .gitignore`));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
program
|
|
71
|
+
.command('init')
|
|
72
|
+
.description('Copy AI context templates into the current working directory.')
|
|
73
|
+
.action(async () => {
|
|
74
|
+
const templatesDir = path.resolve(__dirname, '../templates');
|
|
75
|
+
const targetDir = process.cwd();
|
|
76
|
+
|
|
77
|
+
console.log(chalk.cyan('\n⚙ Initializing AI context templates...\n'));
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
await copyTemplateTree(templatesDir, targetDir, targetDir);
|
|
81
|
+
await updateGitignore(targetDir);
|
|
82
|
+
|
|
83
|
+
console.log(chalk.bold.greenBright('\n✅ Done! AI context templates are ready.\n'));
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error(chalk.red('\n❌ Failed to copy templates:'), err.message);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-scanner
|
|
3
|
+
description: Use proactively after changes touching auth, input handling, or data access to scan for OWASP-class vulnerabilities. Runs in an isolated sandbox with read access to the diff.
|
|
4
|
+
tools: Read, Grep, Glob, Bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Security Scanner Agent
|
|
8
|
+
|
|
9
|
+
## Identity
|
|
10
|
+
You audit code changes for security vulnerabilities. You do not fix issues yourself —
|
|
11
|
+
you report findings for the calling agent or user to act on.
|
|
12
|
+
|
|
13
|
+
## Scope
|
|
14
|
+
- Injection risks: SQL/NoSQL, command, path traversal
|
|
15
|
+
- Auth/authz: missing checks, ownership bypass, token handling
|
|
16
|
+
- Secrets: hardcoded credentials, secrets in logs or error responses
|
|
17
|
+
- Dependency risks: known-vulnerable packages introduced by the change
|
|
18
|
+
|
|
19
|
+
## Process
|
|
20
|
+
1. Read the diff or changed files provided.
|
|
21
|
+
2. Cross-reference against `.claude/rules/security-checklist.md`.
|
|
22
|
+
3. For each finding, report: file, line, the concrete exploit scenario, and severity.
|
|
23
|
+
4. Do not report style or correctness issues outside of security — that's a different agent's job.
|
|
24
|
+
|
|
25
|
+
## Boundaries
|
|
26
|
+
- Read-only: never modify files
|
|
27
|
+
- Never execute code from the diff being audited
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Removes build artifacts and caches so the next install/build starts from a clean slate.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
rm -rf dist build coverage .turbo .next .cache node_modules/.cache
|
|
6
|
+
echo "Cleaned build artifacts and caches. Run 'npm install' if node_modules was affected."
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Builds and starts the local Docker Compose stack in the background.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
if [ ! -f "docker-compose.yml" ] && [ ! -f "compose.yml" ]; then
|
|
6
|
+
echo "No docker-compose.yml/compose.yml found in this project."
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
docker compose up -d --build
|
|
11
|
+
docker compose ps
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Formats the codebase with Prettier (or the project's configured formatter).
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
if npm run format --if-present; then
|
|
6
|
+
exit 0
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
npx --no-install prettier --write . 2>/dev/null || echo "No formatter configured — add a 'format' script or Prettier config."
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Scaffolds a new REST resource: model + controller + route stub.
|
|
3
|
+
# Usage: new-resource.sh <ResourceName> e.g. new-resource.sh Invoice
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
NAME="${1:-}"
|
|
7
|
+
if [ -z "$NAME" ]; then
|
|
8
|
+
echo "Usage: new-resource.sh <ResourceName>"
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
LOWER="$(echo "${NAME:0:1}" | tr '[:upper:]' '[:lower:]')${NAME:1}"
|
|
13
|
+
|
|
14
|
+
mkdir -p models controllers routes
|
|
15
|
+
|
|
16
|
+
cat > "models/${NAME}.js" <<EOF
|
|
17
|
+
import mongoose from 'mongoose';
|
|
18
|
+
|
|
19
|
+
const ${LOWER}Schema = new mongoose.Schema(
|
|
20
|
+
{
|
|
21
|
+
// TODO: define fields
|
|
22
|
+
},
|
|
23
|
+
{ timestamps: true }
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export default mongoose.model('${NAME}', ${LOWER}Schema);
|
|
27
|
+
EOF
|
|
28
|
+
|
|
29
|
+
cat > "controllers/${LOWER}Controller.js" <<EOF
|
|
30
|
+
import ${NAME} from '../models/${NAME}.js';
|
|
31
|
+
|
|
32
|
+
export async function list(req, res) {
|
|
33
|
+
const items = await ${NAME}.find();
|
|
34
|
+
res.json(items);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function getById(req, res) {
|
|
38
|
+
const item = await ${NAME}.findById(req.params.id);
|
|
39
|
+
if (!item) return res.status(404).json({ error: { code: 'NOT_FOUND', message: '${NAME} not found' } });
|
|
40
|
+
res.json(item);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function create(req, res) {
|
|
44
|
+
const item = await ${NAME}.create(req.body);
|
|
45
|
+
res.status(201).json(item);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function update(req, res) {
|
|
49
|
+
const item = await ${NAME}.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
|
50
|
+
if (!item) return res.status(404).json({ error: { code: 'NOT_FOUND', message: '${NAME} not found' } });
|
|
51
|
+
res.json(item);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function remove(req, res) {
|
|
55
|
+
await ${NAME}.findByIdAndDelete(req.params.id);
|
|
56
|
+
res.status(204).end();
|
|
57
|
+
}
|
|
58
|
+
EOF
|
|
59
|
+
|
|
60
|
+
cat > "routes/${LOWER}Routes.js" <<EOF
|
|
61
|
+
import { Router } from 'express';
|
|
62
|
+
import * as ${LOWER}Controller from '../controllers/${LOWER}Controller.js';
|
|
63
|
+
|
|
64
|
+
const router = Router();
|
|
65
|
+
|
|
66
|
+
router.get('/', ${LOWER}Controller.list);
|
|
67
|
+
router.get('/:id', ${LOWER}Controller.getById);
|
|
68
|
+
router.post('/', ${LOWER}Controller.create);
|
|
69
|
+
router.patch('/:id', ${LOWER}Controller.update);
|
|
70
|
+
router.delete('/:id', ${LOWER}Controller.remove);
|
|
71
|
+
|
|
72
|
+
export default router;
|
|
73
|
+
EOF
|
|
74
|
+
|
|
75
|
+
echo "Created models/${NAME}.js, controllers/${LOWER}Controller.js, routes/${LOWER}Routes.js"
|
|
76
|
+
echo "Remember to mount the router in your app entrypoint and fill in the schema fields."
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Automated cleanup/formatting script, fired after Claude modifies a file.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
FILE="${1:-}"
|
|
6
|
+
[ -z "$FILE" ] && exit 0
|
|
7
|
+
|
|
8
|
+
case "$FILE" in
|
|
9
|
+
*.js|*.jsx|*.ts|*.tsx|*.json|*.css)
|
|
10
|
+
npx --no-install prettier --write "$FILE" 2>/dev/null || true
|
|
11
|
+
;;
|
|
12
|
+
esac
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Architectural Documentation Principles
|
|
2
|
+
|
|
3
|
+
- Document decisions, not implementation — code already shows *what*; docs should explain *why*
|
|
4
|
+
- Keep architecture docs close to the code they describe; prefer a short README per module over one giant doc
|
|
5
|
+
- When a doc and the code disagree, the code wins — flag the doc as stale rather than trusting it blindly
|
|
6
|
+
- Record trade-offs considered and rejected, not just the chosen path — future readers need to know what was ruled out and why
|
|
7
|
+
- Avoid documenting anything derivable by reading the code (file layout, obvious naming) — it will rot
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Security Checklist
|
|
2
|
+
|
|
3
|
+
Run through this before finalizing any change that touches input handling, auth, or data access.
|
|
4
|
+
|
|
5
|
+
## Input & Injection
|
|
6
|
+
- All user input is validated/sanitized before use (never trust client data)
|
|
7
|
+
- Database queries use parameterization/ODM query builders — no string-concatenated queries
|
|
8
|
+
- File paths derived from user input are validated against traversal (`../`)
|
|
9
|
+
- Shell commands never interpolate unsanitized user input
|
|
10
|
+
|
|
11
|
+
## Auth & Access Control
|
|
12
|
+
- Every endpoint checks authentication before authorization
|
|
13
|
+
- Authorization checks resource ownership, not just role — a valid token isn't enough
|
|
14
|
+
- Tokens/secrets are never logged, echoed in error messages, or committed to source
|
|
15
|
+
|
|
16
|
+
## Data Handling
|
|
17
|
+
- Secrets and credentials come from environment variables, never hardcoded
|
|
18
|
+
- Sensitive fields (passwords, tokens) are hashed/encrypted at rest, never stored plaintext
|
|
19
|
+
- Responses don't leak internal details (stack traces, DB errors) to the client
|
|
20
|
+
|
|
21
|
+
## Dependencies
|
|
22
|
+
- New dependencies are checked for maintenance status and known CVEs before adding
|
|
23
|
+
- Prefer built-in APIs over adding a dependency for something trivial
|
|
24
|
+
|
|
25
|
+
Flag any violation explicitly with a `⚠️ Security:` prefix rather than silently fixing or ignoring it.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: testing-patterns
|
|
3
|
+
description: Conventions for writing and running tests in this project — use when adding or modifying tests.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Testing Patterns
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
When writing new tests, fixing failing tests, or asked to add test coverage.
|
|
10
|
+
|
|
11
|
+
## Conventions
|
|
12
|
+
- Test files live next to the code they test, or under a mirrored `__tests__`/`test` directory — match whatever the repo already does
|
|
13
|
+
- One behavior per test; test names describe the behavior, not the implementation
|
|
14
|
+
- Use real dependencies where feasible; mock only true external boundaries (network, third-party APIs, time)
|
|
15
|
+
- Cover the golden path plus the edge cases that actually matter for this code — not exhaustive permutations
|
|
16
|
+
- A failing test must fail for the reason it claims to — verify by temporarily breaking the code under test
|
|
17
|
+
|
|
18
|
+
## Running tests
|
|
19
|
+
```bash
|
|
20
|
+
npm test # full suite
|
|
21
|
+
npm test -- <pattern> # filtered run
|
|
22
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Deployment Guide
|
|
2
|
+
|
|
3
|
+
Strict, chronological steps for shipping a release. Do not reorder or skip steps.
|
|
4
|
+
|
|
5
|
+
1. Confirm `main` is green in CI (build, lint, test all passing)
|
|
6
|
+
2. Bump the version (`npm version <patch|minor|major>`)
|
|
7
|
+
3. Update the changelog with user-facing changes since the last release
|
|
8
|
+
4. Push the version commit and tag: `git push && git push --tags`
|
|
9
|
+
5. Confirm the release workflow in GitHub Actions builds and pushes the Docker image
|
|
10
|
+
6. Deploy to staging first; run smoke tests against staging
|
|
11
|
+
7. Promote staging to production
|
|
12
|
+
8. Watch error/latency dashboards for 15 minutes post-deploy before considering the deploy complete
|
|
13
|
+
9. If anything regresses, roll back immediately rather than attempting a forward fix under pressure
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Cursor AI Rules (legacy fallback — prefer .cursor/rules/*.mdc)
|
|
2
|
+
|
|
3
|
+
## Project Philosophy
|
|
4
|
+
- Write clean, readable, and maintainable code above all else.
|
|
5
|
+
- Prefer explicit over implicit; avoid magic unless well-documented.
|
|
6
|
+
- Follow SOLID principles and separation of concerns.
|
|
7
|
+
|
|
8
|
+
## Code Style
|
|
9
|
+
- Use `async/await` — never raw `.then()/.catch()` chains
|
|
10
|
+
- Always destructure imports: `import { foo } from 'bar'`
|
|
11
|
+
- Prefer `const` > `let`; never use `var`
|
|
12
|
+
- File names: `kebab-case.js`, component names: `PascalCase.jsx`
|
|
13
|
+
- Max function length: ~40 lines. Extract if longer.
|
|
14
|
+
|
|
15
|
+
## Error Handling
|
|
16
|
+
- All async functions must have a `try/catch` block
|
|
17
|
+
- Never silently swallow errors — always log or rethrow
|
|
18
|
+
- Use structured error objects: `{ code, message, details }`
|
|
19
|
+
|
|
20
|
+
## AI Behavior
|
|
21
|
+
- Do not generate commented-out code
|
|
22
|
+
- Do not add unnecessary boilerplate or placeholder TODO blocks
|
|
23
|
+
- When refactoring, touch only the requested scope
|
|
24
|
+
- Always suggest the simplest solution first
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Removes build artifacts and caches so the next install/build starts from a clean slate.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
rm -rf dist build coverage .turbo .next .cache node_modules/.cache
|
|
6
|
+
echo "Cleaned build artifacts and caches. Run 'npm install' if node_modules was affected."
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Builds and starts the local Docker Compose stack in the background.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
if [ ! -f "docker-compose.yml" ] && [ ! -f "compose.yml" ]; then
|
|
6
|
+
echo "No docker-compose.yml/compose.yml found in this project."
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
docker compose up -d --build
|
|
11
|
+
docker compose ps
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Formats the codebase with Prettier (or the project's configured formatter).
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
if npm run format --if-present; then
|
|
6
|
+
exit 0
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
npx --no-install prettier --write . 2>/dev/null || echo "No formatter configured — add a 'format' script or Prettier config."
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Scaffolds a new REST resource: model + controller + route stub.
|
|
3
|
+
# Usage: new-resource.sh <ResourceName> e.g. new-resource.sh Invoice
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
NAME="${1:-}"
|
|
7
|
+
if [ -z "$NAME" ]; then
|
|
8
|
+
echo "Usage: new-resource.sh <ResourceName>"
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
LOWER="$(echo "${NAME:0:1}" | tr '[:upper:]' '[:lower:]')${NAME:1}"
|
|
13
|
+
|
|
14
|
+
mkdir -p models controllers routes
|
|
15
|
+
|
|
16
|
+
cat > "models/${NAME}.js" <<EOF
|
|
17
|
+
import mongoose from 'mongoose';
|
|
18
|
+
|
|
19
|
+
const ${LOWER}Schema = new mongoose.Schema(
|
|
20
|
+
{
|
|
21
|
+
// TODO: define fields
|
|
22
|
+
},
|
|
23
|
+
{ timestamps: true }
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export default mongoose.model('${NAME}', ${LOWER}Schema);
|
|
27
|
+
EOF
|
|
28
|
+
|
|
29
|
+
cat > "controllers/${LOWER}Controller.js" <<EOF
|
|
30
|
+
import ${NAME} from '../models/${NAME}.js';
|
|
31
|
+
|
|
32
|
+
export async function list(req, res) {
|
|
33
|
+
const items = await ${NAME}.find();
|
|
34
|
+
res.json(items);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function getById(req, res) {
|
|
38
|
+
const item = await ${NAME}.findById(req.params.id);
|
|
39
|
+
if (!item) return res.status(404).json({ error: { code: 'NOT_FOUND', message: '${NAME} not found' } });
|
|
40
|
+
res.json(item);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function create(req, res) {
|
|
44
|
+
const item = await ${NAME}.create(req.body);
|
|
45
|
+
res.status(201).json(item);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function update(req, res) {
|
|
49
|
+
const item = await ${NAME}.findByIdAndUpdate(req.params.id, req.body, { new: true });
|
|
50
|
+
if (!item) return res.status(404).json({ error: { code: 'NOT_FOUND', message: '${NAME} not found' } });
|
|
51
|
+
res.json(item);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function remove(req, res) {
|
|
55
|
+
await ${NAME}.findByIdAndDelete(req.params.id);
|
|
56
|
+
res.status(204).end();
|
|
57
|
+
}
|
|
58
|
+
EOF
|
|
59
|
+
|
|
60
|
+
cat > "routes/${LOWER}Routes.js" <<EOF
|
|
61
|
+
import { Router } from 'express';
|
|
62
|
+
import * as ${LOWER}Controller from '../controllers/${LOWER}Controller.js';
|
|
63
|
+
|
|
64
|
+
const router = Router();
|
|
65
|
+
|
|
66
|
+
router.get('/', ${LOWER}Controller.list);
|
|
67
|
+
router.get('/:id', ${LOWER}Controller.getById);
|
|
68
|
+
router.post('/', ${LOWER}Controller.create);
|
|
69
|
+
router.patch('/:id', ${LOWER}Controller.update);
|
|
70
|
+
router.delete('/:id', ${LOWER}Controller.remove);
|
|
71
|
+
|
|
72
|
+
export default router;
|
|
73
|
+
EOF
|
|
74
|
+
|
|
75
|
+
echo "Created models/${NAME}.js, controllers/${LOWER}Controller.js, routes/${LOWER}Routes.js"
|
|
76
|
+
echo "Remember to mount the router in your app entrypoint and fill in the schema fields."
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fires automatically after the AI modifies a file.
|
|
3
|
+
# Keep this fast — it runs synchronously in the edit loop.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
FILE="${1:-}"
|
|
7
|
+
[ -z "$FILE" ] && exit 0
|
|
8
|
+
|
|
9
|
+
case "$FILE" in
|
|
10
|
+
*.js|*.jsx|*.ts|*.tsx)
|
|
11
|
+
npx --no-install prettier --write "$FILE" 2>/dev/null || true
|
|
12
|
+
;;
|
|
13
|
+
esac
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Backend/API conventions
|
|
3
|
+
globs:
|
|
4
|
+
- "server/**/*"
|
|
5
|
+
- "backend/**/*"
|
|
6
|
+
- "**/controllers/**/*"
|
|
7
|
+
- "**/routes/**/*"
|
|
8
|
+
- "**/models/**/*"
|
|
9
|
+
alwaysApply: false
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Backend Rules
|
|
13
|
+
|
|
14
|
+
- API routes follow REST conventions: `GET /api/resource`, `POST /api/resource`, `PATCH /api/resource/:id`
|
|
15
|
+
- Controllers stay thin — business logic lives in services, not route handlers
|
|
16
|
+
- Every route handler validates input before touching the database
|
|
17
|
+
- Never trust client-supplied IDs for authorization — always check ownership server-side
|
|
18
|
+
- Return consistent error shapes: `{ error: { code, message } }`
|
|
19
|
+
- Log errors with enough context to reproduce (request id, user id, input shape) — never log secrets
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# UI Conventions (applies globally to frontend assets)
|
|
2
|
+
|
|
3
|
+
- Components are function components using hooks — no class components
|
|
4
|
+
- Co-locate component styles: `Button.jsx` + `Button.module.css`
|
|
5
|
+
- Keep components under ~150 lines; extract subcomponents or hooks when larger
|
|
6
|
+
- Derive state where possible instead of syncing it with `useEffect`
|
|
7
|
+
- Accessibility is not optional: interactive elements need proper roles, labels, and keyboard support
|
|
8
|
+
- Avoid inline styles except for values computed at runtime
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-reviewer
|
|
3
|
+
description: Reviews a diff for correctness bugs, security issues, and unnecessary complexity before it's committed.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Code Reviewer
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
Invoke before committing or opening a PR, or when explicitly asked to review changes.
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
1. Get the diff (`git diff` for unstaged, `git diff --staged` for staged).
|
|
13
|
+
2. Check correctness: does the change do what it claims, including edge cases?
|
|
14
|
+
3. Check security: input validation, auth checks, injection risks (see `.claude/rules/security-checklist.md` if present).
|
|
15
|
+
4. Check scope: does the diff touch only what's needed, or drag in unrelated changes?
|
|
16
|
+
5. Report findings ranked by severity. Don't restate the diff — only flag actionable issues.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Project Context
|
|
2
|
+
|
|
3
|
+
## Role
|
|
4
|
+
You are a senior full-stack engineer working within this codebase.
|
|
5
|
+
Your primary goals are correctness, simplicity, and developer ergonomics.
|
|
6
|
+
|
|
7
|
+
## Stack
|
|
8
|
+
<!-- Replace with your actual stack. Example (MERN): -->
|
|
9
|
+
- **Frontend:** React 18 SPA, Vite bundler, TailwindCSS
|
|
10
|
+
- **Backend:** Node.js + Express REST API, ES Modules
|
|
11
|
+
- **Database:** MongoDB Atlas, Mongoose schemas
|
|
12
|
+
- **Auth:** JWT access tokens (15min) + HTTP-only refresh tokens (7d)
|
|
13
|
+
- **Deployment:** Docker + GitHub Actions CI/CD
|
|
14
|
+
|
|
15
|
+
## Build, Test, Lint
|
|
16
|
+
```bash
|
|
17
|
+
npm install # install dependencies
|
|
18
|
+
npm run dev # start local dev server
|
|
19
|
+
npm test # run test suite
|
|
20
|
+
npm run lint # run linter
|
|
21
|
+
npm run build # production build
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Coding Conventions
|
|
25
|
+
- Match the existing code style in the file you're editing over any generic default
|
|
26
|
+
- Prefer small, focused functions and modules over large ones
|
|
27
|
+
- Keep naming consistent with surrounding code (case style, verb/noun patterns)
|
|
28
|
+
- Validate configuration and environment variables at startup, not at point of use
|
|
29
|
+
- Don't introduce new dependencies when the standard library or an existing dependency covers the need
|
|
30
|
+
|
|
31
|
+
## Response Guidelines
|
|
32
|
+
- Respond with working code, not pseudocode
|
|
33
|
+
- When modifying existing files, show only the changed diff, not the entire file
|
|
34
|
+
- If a request is ambiguous, ask one clarifying question before proceeding
|
|
35
|
+
- Flag security risks explicitly with a `⚠️ Security:` prefix
|
|
36
|
+
- Prefer built-in APIs over adding new dependencies
|
|
37
|
+
|
|
38
|
+
## Out of Scope
|
|
39
|
+
- Do not suggest switching the core stack unless a critical flaw is identified
|
|
40
|
+
- Do not refactor unrelated code in the same response
|
|
41
|
+
- Do not generate files containing real secrets or credentials
|
|
42
|
+
|
|
43
|
+
## See Also
|
|
44
|
+
- [.claude/rules/](.claude/rules/) — additional context-injected rules (architecture, security)
|
|
45
|
+
- [.claude/workflows/deployment-guide.md](.claude/workflows/deployment-guide.md) — deployment procedure
|
|
46
|
+
- `claude.local.md` — your personal, git-ignored local notes (not committed)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Local Notes (git-ignored)
|
|
2
|
+
|
|
3
|
+
Personal, machine-specific context for working with Claude Code on this project.
|
|
4
|
+
This file is not committed — use it for scratch notes, WIP context, or preferences
|
|
5
|
+
you don't want to impose on the rest of the team.
|
|
6
|
+
|
|
7
|
+
## Examples of what to put here
|
|
8
|
+
- Local environment quirks (ports, service URLs, test accounts)
|
|
9
|
+
- In-progress investigation notes
|
|
10
|
+
- Personal shortcuts or aliases you want Claude to know about
|
|
11
|
+
- Anything from CLAUDE.md you want to temporarily override for yourself
|