@financebuddha/standards 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FinanceBuddha
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,130 @@
1
+ # @financebuddha/standards
2
+
3
+ Shared engineering standards for all FinanceBuddha repositories — one installed
4
+ package that every repo pulls its ESLint, TypeScript, Prettier, commitlint,
5
+ secrets-scanning, and Claude-skill config from. Upgrade the package, upgrade
6
+ every rule.
7
+
8
+ ## Install
9
+
10
+ Published to the public npm registry under the `@financebuddha` scope:
11
+
12
+ ```bash
13
+ # bun
14
+ bun add -D @financebuddha/standards
15
+
16
+ # npm
17
+ npm install --save-dev @financebuddha/standards
18
+ ```
19
+
20
+ > Installing from GitHub directly (e.g. to test an unpublished commit) also
21
+ > works: `npm i -D github:financebuddha/engineering-standards#<ref>`.
22
+
23
+ ## Quick start
24
+
25
+ After installing, scaffold the thin re-export config files into your project:
26
+
27
+ ```bash
28
+ npx @financebuddha/standards init # writes config files (skips existing)
29
+ npx @financebuddha/standards init --force # overwrite existing
30
+ npx @financebuddha/standards init --skill # also install the Claude skill
31
+ ```
32
+
33
+ `init` writes files that **re-export** from the installed package, so nothing is
34
+ duplicated. It prints the two manual one-liners (tsconfig + Prettier) at the end.
35
+
36
+ ## What's inside
37
+
38
+ | Subpath / file | How you use it |
39
+ |---|---|
40
+ | `@financebuddha/standards/eslint/nextjs` | `export { default } from '@financebuddha/standards/eslint/nextjs'` in `eslint.config.mjs` |
41
+ | `@financebuddha/standards/typescript/nextjs` | `{ "extends": "@financebuddha/standards/typescript/nextjs" }` in `tsconfig.json` |
42
+ | `@financebuddha/standards/typescript/base` | tsconfig base for non-Next.js packages |
43
+ | `@financebuddha/standards/prettier` | `"prettier": "@financebuddha/standards/prettier"` in `package.json` |
44
+ | `@financebuddha/standards/commitlint` | `export { default } from '@financebuddha/standards/commitlint'` in `commitlint.config.js` |
45
+ | `secrets-patterns.txt` | grep patterns for the secrets scan (CI + husky hook) |
46
+ | `husky/*` | pre-commit (secrets scan + lint-staged) and commit-msg (commitlint) hooks |
47
+ | `skills/pre-commit-checks.md` | Claude skill: secrets → lint → typecheck → tests |
48
+
49
+ ## Manual reference (what `init` automates)
50
+
51
+ ### ESLint — `eslint.config.mjs`
52
+ ```js
53
+ export { default } from '@financebuddha/standards/eslint/nextjs'
54
+
55
+ // To add project rules, spread the preset instead:
56
+ // import base from '@financebuddha/standards/eslint/nextjs'
57
+ // export default [...base, { rules: { 'no-console': 'warn' } }]
58
+ ```
59
+
60
+ ### TypeScript — `tsconfig.json`
61
+ ```json
62
+ {
63
+ "extends": "@financebuddha/standards/typescript/nextjs",
64
+ "compilerOptions": { "paths": { "@/*": ["./*"] } }
65
+ }
66
+ ```
67
+
68
+ ### Prettier — `package.json`
69
+ ```json
70
+ { "prettier": "@financebuddha/standards/prettier" }
71
+ ```
72
+
73
+ ### commitlint — `commitlint.config.js`
74
+ ```js
75
+ export { default } from '@financebuddha/standards/commitlint'
76
+ ```
77
+
78
+ ### Husky — `package.json`
79
+ ```json
80
+ { "scripts": { "prepare": "husky" } }
81
+ ```
82
+ Then `npx @financebuddha/standards init` drops the hooks into `.husky/`.
83
+
84
+ ### Claude skill
85
+ ```bash
86
+ npx @financebuddha/standards init --skill
87
+ # copies skills/pre-commit-checks.md → ~/.claude/skills/
88
+ # invoke with /pre-commit-checks in any Claude Code session
89
+ ```
90
+
91
+ ## Secrets scanning
92
+
93
+ `secrets-patterns.txt` is the single source of truth — referenced by both the
94
+ husky `pre-commit` hook and CI (`.github/workflows/ci.yml`). Add a pattern once
95
+ and both layers pick it up. Covers AWS keys, OpenAI/GitHub/Slack tokens, private
96
+ key blocks, JWTs, Google API keys, and generic `secret = "…"` assignments.
97
+
98
+ ## Releasing a new version
99
+
100
+ Releases are automated with [release-please](https://github.com/googleapis/release-please)
101
+ driven by conventional commits — you never bump the version by hand.
102
+
103
+ 1. Merge conventional commits to `main` (`feat:`, `fix:`, etc.).
104
+ 2. release-please opens/updates a **"chore(main): release x.y.z"** PR that bumps
105
+ `package.json` + `.release-please-manifest.json` and writes `CHANGELOG.md`.
106
+ - `feat:` → minor (patch while < 1.0), `fix:` → patch, `feat!:`/`BREAKING CHANGE` → major.
107
+ 3. Merge that PR. release-please creates the git tag + GitHub Release, which
108
+ triggers the **publish** job → `npm publish --provenance --access public`.
109
+
110
+ Consuming repos pick up new rules with `npm update @financebuddha/standards`
111
+ (or bump the version range in `package.json`).
112
+
113
+ ### One-time setup for publishing
114
+
115
+ 1. **npm org** — an npm org named **`financebuddha`** must exist (owns the
116
+ `@financebuddha` scope). Create it at npmjs.com → Add Organization.
117
+ 2. **`NPM_TOKEN` secret** — an npm **Automation** access token with publish
118
+ rights to the scope, added under Settings → Secrets and variables → Actions.
119
+ 3. **release-please token** — this org disables write access for the default
120
+ Actions token, so add a **`RELEASE_PLEASE_TOKEN`** secret: a fine-grained PAT
121
+ (or classic PAT with `repo` + `workflow` scope) that can open PRs and push
122
+ tags on this repo. _(If an org admin instead enables Settings → Actions →
123
+ "Workflow permissions: read and write" + "Allow Actions to create and approve
124
+ pull requests", the workflow falls back to the built-in token and this secret
125
+ becomes unnecessary.)_
126
+
127
+ ## Commit convention
128
+
129
+ All commits follow [Conventional Commits](https://www.conventionalcommits.org/).
130
+ See `commitlint/config.ts` for the annotated ruleset.
package/bin/init.mjs ADDED
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @financebuddha/standards — project initializer
4
+ *
5
+ * npx @financebuddha/standards init [options]
6
+ *
7
+ * Scaffolds the thin "re-export" config files into the current project so that
8
+ * every shared rule is pulled live from this installed package (node_modules) —
9
+ * nothing is duplicated, so an upgrade of the package upgrades every rule.
10
+ *
11
+ * Files it can write (each skipped if it already exists, unless --force):
12
+ * eslint.config.mjs → re-exports @financebuddha/standards/eslint/nextjs
13
+ * .prettierrc → { "...": "@financebuddha/standards/prettier" } note below
14
+ * commitlint.config.js → re-exports @financebuddha/standards/commitlint
15
+ * .husky/pre-commit → secrets scan + lint-staged
16
+ * .husky/commit-msg → commitlint
17
+ * secrets-patterns.txt → copied to project root (husky hook reads it)
18
+ *
19
+ * Flags:
20
+ * --force overwrite files that already exist
21
+ * --skill also copy the pre-commit-checks Claude skill to ~/.claude/skills/
22
+ * --no-husky skip writing husky hooks
23
+ * --help show this help
24
+ *
25
+ * Note on Prettier: Prettier cannot "extend" a config the way ESLint/TS can, so
26
+ * the recommended approach is to set "prettier": "@financebuddha/standards/prettier"
27
+ * in your package.json. This script prints that snippet rather than guessing how
28
+ * to edit your package.json.
29
+ */
30
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync, chmodSync } from 'node:fs'
31
+ import { dirname, join, resolve } from 'node:path'
32
+ import { fileURLToPath } from 'node:url'
33
+ import { homedir } from 'node:os'
34
+
35
+ const PKG = '@financebuddha/standards'
36
+ const __dirname = dirname(fileURLToPath(import.meta.url))
37
+ const PKG_ROOT = resolve(__dirname, '..')
38
+ const CWD = process.cwd()
39
+
40
+ const args = new Set(process.argv.slice(2))
41
+ const FORCE = args.has('--force')
42
+ const WANT_SKILL = args.has('--skill')
43
+ const NO_HUSKY = args.has('--no-husky')
44
+
45
+ if (args.has('--help') || args.has('-h')) {
46
+ console.log(readFileSync(fileURLToPath(import.meta.url), 'utf8').split('*/')[0].replace(/^#![^\n]*\n/, '').replace(/\/\*\*?|^ \* ?/gm, ''))
47
+ process.exit(0)
48
+ }
49
+
50
+ const written = []
51
+ const skipped = []
52
+
53
+ /** Write a file unless it exists (or --force). Track outcome for the summary. */
54
+ function place(relPath, contents, { exec = false } = {}) {
55
+ const dest = join(CWD, relPath)
56
+ if (existsSync(dest) && !FORCE) {
57
+ skipped.push(relPath)
58
+ return
59
+ }
60
+ mkdirSync(dirname(dest), { recursive: true })
61
+ writeFileSync(dest, contents)
62
+ if (exec) chmodSync(dest, 0o755)
63
+ written.push(relPath)
64
+ }
65
+
66
+ // ── eslint.config.mjs ─────────────────────────────────────────────────────────
67
+ place(
68
+ 'eslint.config.mjs',
69
+ `// Pulled from ${PKG}. Add project-specific rules by spreading the preset.\n` +
70
+ `export { default } from '${PKG}/eslint/nextjs'\n`
71
+ )
72
+
73
+ // ── commitlint.config.js ──────────────────────────────────────────────────────
74
+ place(
75
+ 'commitlint.config.js',
76
+ `// Pulled from ${PKG}.\n` + `export { default } from '${PKG}/commitlint'\n`
77
+ )
78
+
79
+ // ── secrets-patterns.txt (copied — husky hook greps it locally) ───────────────
80
+ {
81
+ const dest = join(CWD, 'secrets-patterns.txt')
82
+ if (existsSync(dest) && !FORCE) {
83
+ skipped.push('secrets-patterns.txt')
84
+ } else {
85
+ copyFileSync(join(PKG_ROOT, 'secrets-patterns.txt'), dest)
86
+ written.push('secrets-patterns.txt')
87
+ }
88
+ }
89
+
90
+ // ── husky hooks ───────────────────────────────────────────────────────────────
91
+ if (!NO_HUSKY) {
92
+ place('.husky/pre-commit', readFileSync(join(PKG_ROOT, 'husky/pre-commit'), 'utf8'), { exec: true })
93
+ place('.husky/commit-msg', readFileSync(join(PKG_ROOT, 'husky/commit-msg'), 'utf8'), { exec: true })
94
+ }
95
+
96
+ // ── Claude skill (opt-in) ─────────────────────────────────────────────────────
97
+ if (WANT_SKILL) {
98
+ const skillsDir = join(homedir(), '.claude', 'skills')
99
+ const dest = join(skillsDir, 'pre-commit-checks.md')
100
+ if (existsSync(dest) && !FORCE) {
101
+ skipped.push('~/.claude/skills/pre-commit-checks.md')
102
+ } else {
103
+ mkdirSync(skillsDir, { recursive: true })
104
+ copyFileSync(join(PKG_ROOT, 'skills/pre-commit-checks.md'), dest)
105
+ written.push('~/.claude/skills/pre-commit-checks.md')
106
+ }
107
+ }
108
+
109
+ // ── Summary ───────────────────────────────────────────────────────────────────
110
+ console.log(`\n${PKG} init\n${'─'.repeat(40)}`)
111
+ if (written.length) {
112
+ console.log('Written:')
113
+ written.forEach((f) => console.log(` + ${f}`))
114
+ }
115
+ if (skipped.length) {
116
+ console.log('Skipped (already exist — pass --force to overwrite):')
117
+ skipped.forEach((f) => console.log(` · ${f}`))
118
+ }
119
+
120
+ console.log('\nManual steps:')
121
+ console.log(' 1. tsconfig.json — add:')
122
+ console.log(` { "extends": "${PKG}/typescript/nextjs" }`)
123
+ console.log(' 2. package.json — add for Prettier:')
124
+ console.log(` "prettier": "${PKG}/prettier"`)
125
+ console.log(' 3. Ensure husky is installed and "prepare": "husky" is in package.json scripts.')
126
+ console.log('')
@@ -0,0 +1,21 @@
1
+ import type { UserConfig } from '@commitlint/types'
2
+
3
+ const config: UserConfig = {
4
+ extends: ['@commitlint/config-conventional'],
5
+ rules: {
6
+ 'type-enum': [
7
+ 2, 'always',
8
+ ['feat','fix','docs','style','refactor','perf','test','build','ci','chore','revert'],
9
+ ],
10
+ 'subject-empty': [2, 'never'],
11
+ 'subject-full-stop': [2, 'never', '.'],
12
+ 'subject-case': [2, 'always', 'lower-case'],
13
+ 'type-case': [2, 'always', 'lower-case'],
14
+ 'header-max-length': [2, 'always', 100],
15
+ 'body-leading-blank': [1, 'always'],
16
+ 'footer-leading-blank':[1, 'always'],
17
+ 'scope-case': [2, 'always', 'lower-case'],
18
+ },
19
+ }
20
+
21
+ export default config
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Shared commitlint config for FinanceBuddha repos.
3
+ *
4
+ * Importable (no TypeScript loader required). In your commitlint.config.js:
5
+ * export { default } from '@financebuddha/standards/commitlint'
6
+ *
7
+ * Or extend it in commitlint.config.ts:
8
+ * import base from '@financebuddha/standards/commitlint'
9
+ * export default { ...base }
10
+ *
11
+ * The annotated TypeScript source of record lives in ./config.ts.
12
+ */
13
+ const config = {
14
+ extends: ['@commitlint/config-conventional'],
15
+ rules: {
16
+ 'type-enum': [
17
+ 2,
18
+ 'always',
19
+ ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert'],
20
+ ],
21
+ 'subject-empty': [2, 'never'],
22
+ 'subject-full-stop': [2, 'never', '.'],
23
+ 'subject-case': [2, 'always', 'lower-case'],
24
+ 'type-case': [2, 'always', 'lower-case'],
25
+ 'header-max-length': [2, 'always', 100],
26
+ 'body-leading-blank': [1, 'always'],
27
+ 'footer-leading-blank': [1, 'always'],
28
+ 'scope-case': [2, 'always', 'lower-case'],
29
+ },
30
+ }
31
+
32
+ export default config
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Shared ESLint flat config for FinanceBuddha Next.js projects.
3
+ *
4
+ * Usage in eslint.config.mjs:
5
+ * import config from './node_modules/@financebuddha/standards/eslint/nextjs.mjs'
6
+ * export default config
7
+ */
8
+ import { defineConfig, globalIgnores } from "eslint/config";
9
+ import nextVitals from "eslint-config-next/core-web-vitals";
10
+ import nextTs from "eslint-config-next/typescript";
11
+
12
+ export default defineConfig([
13
+ ...nextVitals,
14
+ ...nextTs,
15
+ globalIgnores([
16
+ ".next/**",
17
+ "out/**",
18
+ "build/**",
19
+ "next-env.d.ts",
20
+ "scripts/**",
21
+ "skills/**",
22
+ "coverage/**",
23
+ ]),
24
+ ]);
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env sh
2
+
3
+ if command -v bunx >/dev/null 2>&1; then
4
+ bunx --no -- commitlint --edit "$1"
5
+ else
6
+ npx --no -- commitlint --edit "$1"
7
+ fi
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env sh
2
+
3
+ # Resolve the repo root so the patterns file is found regardless of where the
4
+ # hook runs from. Prefer a project-root secrets-patterns.txt (written by
5
+ # `npx @financebuddha/standards init`), else fall back to the one shipped in
6
+ # node_modules.
7
+ ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo ".")
8
+ PATTERNS="$ROOT/secrets-patterns.txt"
9
+ if [ ! -f "$PATTERNS" ]; then
10
+ PATTERNS="$ROOT/node_modules/@financebuddha/standards/secrets-patterns.txt"
11
+ fi
12
+
13
+ # Secrets scan on staged changes. Strip comment/blank lines from the patterns
14
+ # file first — grep -f treats every line (including '# AWS') as a regex, which
15
+ # would otherwise match the comments themselves.
16
+ if [ -f "$PATTERNS" ]; then
17
+ CLEAN=$(grep -vE '^[[:space:]]*#|^[[:space:]]*$' "$PATTERNS")
18
+ if [ -n "$CLEAN" ]; then
19
+ SECRETS=$(git diff --cached -U0 | grep -E "$CLEAN" 2>/dev/null)
20
+ if [ -n "$SECRETS" ]; then
21
+ echo "❌ Possible secrets detected in staged changes:"
22
+ echo "$SECRETS"
23
+ echo ""
24
+ echo "Review the matches above. If they are false positives, remove the"
25
+ echo "matching lines or run 'git commit --no-verify' to bypass (use sparingly)."
26
+ exit 1
27
+ fi
28
+ fi
29
+ fi
30
+
31
+ # Run lint-staged (use bunx if available, else npx)
32
+ if command -v bunx >/dev/null 2>&1; then
33
+ bunx lint-staged
34
+ else
35
+ npx lint-staged
36
+ fi
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@financebuddha/standards",
3
+ "version": "0.1.0",
4
+ "description": "Shared engineering standards: ESLint, TypeScript, Prettier, commitlint, secrets scanning, and Claude skills",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/financebuddha/engineering-standards.git"
13
+ },
14
+ "homepage": "https://github.com/financebuddha/engineering-standards#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/financebuddha/engineering-standards/issues"
17
+ },
18
+ "engines": {
19
+ "node": ">=18"
20
+ },
21
+ "exports": {
22
+ "./eslint/nextjs": "./eslint/nextjs.mjs",
23
+ "./typescript/nextjs": "./typescript/nextjs.json",
24
+ "./typescript/base": "./typescript/base.json",
25
+ "./prettier": "./prettier/config.json",
26
+ "./commitlint": "./commitlint/index.js",
27
+ "./secrets-patterns": "./secrets-patterns.txt",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "bin": {
31
+ "fb-standards": "bin/init.mjs"
32
+ },
33
+ "files": [
34
+ "bin",
35
+ "eslint",
36
+ "typescript",
37
+ "prettier",
38
+ "commitlint",
39
+ "skills",
40
+ "husky",
41
+ "secrets-patterns.txt"
42
+ ],
43
+ "scripts": {
44
+ "init": "node bin/init.mjs"
45
+ },
46
+ "keywords": [
47
+ "eslint-config",
48
+ "tsconfig",
49
+ "prettier-config",
50
+ "commitlint-config",
51
+ "financebuddha",
52
+ "engineering-standards"
53
+ ],
54
+ "peerDependencies": {
55
+ "eslint": ">=9",
56
+ "eslint-config-next": ">=15",
57
+ "prettier": ">=3",
58
+ "typescript": ">=5"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "eslint": { "optional": true },
62
+ "eslint-config-next": { "optional": true },
63
+ "prettier": { "optional": true },
64
+ "typescript": { "optional": true }
65
+ }
66
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "semi": false,
3
+ "singleQuote": true,
4
+ "tabWidth": 2,
5
+ "trailingComma": "es5",
6
+ "printWidth": 100,
7
+ "plugins": []
8
+ }
@@ -0,0 +1,32 @@
1
+ # FinanceBuddha secret scan patterns
2
+ # Used by CI (grep -Ef secrets-patterns.txt) and the pre-commit-checks Claude skill.
3
+ # Each line is a regex matched against git diff output.
4
+
5
+ # AWS
6
+ AKIA[0-9A-Z]{16}
7
+
8
+ # OpenAI
9
+ sk-[A-Za-z0-9]{20,}
10
+
11
+ # GitHub tokens
12
+ ghp_[A-Za-z0-9]{36}
13
+ gho_[A-Za-z0-9]{36}
14
+ github_pat_[A-Za-z0-9_]{82}
15
+
16
+ # Slack
17
+ xox[baprs]-[A-Za-z0-9-]{10,}
18
+
19
+ # Private keys
20
+ -----BEGIN (RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY
21
+
22
+ # JWT (3-part base64url)
23
+ eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}
24
+
25
+ # Google API keys
26
+ AIza[0-9A-Za-z_-]{35}
27
+
28
+ # Google OAuth client ID
29
+ [0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com
30
+
31
+ # Generic credential assignments (password/secret/key = "value")
32
+ (password|passwd|pwd|secret|api_key|apikey|api-key|auth_token|access_token|client_secret|private_key|encryption_key)\s*[:=]\s*["'][^"']{8,}["']
@@ -0,0 +1,167 @@
1
+ ---
2
+ name: pre-commit-checks
3
+ description: >
4
+ Run the full pre-commit quality gate: secrets scan → lint → typecheck → tests.
5
+ Auto-fixes lint errors where possible, diagnoses and fixes failing tests, then
6
+ reports a timed pass/fail summary. Use this skill before every commit or release,
7
+ when asked to "check everything", "run pre-commit", "make sure it's clean",
8
+ "ready to commit?", or any time you want to verify the codebase is in a shippable
9
+ state. Also use proactively after making multiple code changes in one session.
10
+ ---
11
+
12
+ # Pre-Commit Checks
13
+
14
+ Run every step in order. Do not skip ahead. A later step failing does not
15
+ excuse an earlier step — fix each issue in the step where it appears before
16
+ moving on.
17
+
18
+ ## Step 1 — Detect the package manager and project root
19
+
20
+ ```bash
21
+ # Prefer bun if present, otherwise npm/yarn
22
+ cd <project-root>
23
+ which bun && RUNNER="bun run" || RUNNER="npm run"
24
+ ```
25
+
26
+ For the shield project the runner is always `bun run`.
27
+
28
+ ## Step 2 — Secrets scan
29
+
30
+ Scan staged files for accidental secrets before any code reaches the remote.
31
+ Run all three checks; report every hit — do not stop at the first one.
32
+
33
+ ### 2a — Blocked file types (binary / credential files that should never commit)
34
+
35
+ ```bash
36
+ git diff --cached --name-only | grep -Ei \
37
+ '\.(pem|p8|p12|pfx|key|keystore|jks|cer|crt|der|ppk|asc)$|^\.env(\.|$)|id_rsa|id_ed25519|id_ecdsa|id_dsa' \
38
+ && echo "BLOCKED FILES FOUND" || true
39
+ ```
40
+
41
+ Any match is an **automatic FAIL**. Instruct the user to `git reset HEAD <file>`
42
+ and add the pattern to `.gitignore` before proceeding.
43
+
44
+ ### 2b — High-confidence secret patterns in staged content
45
+
46
+ ```bash
47
+ git diff --cached -U0 | grep -nE \
48
+ 'AKIA[0-9A-Z]{16}|sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{36}|gho_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{82}|xox[baprs]-[A-Za-z0-9-]{10,}|-----BEGIN (RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY|eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}|AIza[0-9A-Za-z_-]{35}|[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com' \
49
+ && echo "POSSIBLE SECRETS FOUND" || echo "No high-confidence secrets detected"
50
+ ```
51
+
52
+ Any match: surface it to the user verbatim, identify the file and line number
53
+ from the diff context, and **stop** — do not commit. The user must review and
54
+ either remove the secret or confirm it is a false positive (e.g. test fixture
55
+ with a clearly fake value like `sk-test-fake-key-for-unit-tests`).
56
+
57
+ ### 2c — Generic high-entropy / common key patterns in staged content
58
+
59
+ ```bash
60
+ git diff --cached -U0 | grep -nEi \
61
+ '(password|passwd|pwd|secret|api_key|apikey|api-key|auth_token|access_token|client_secret|private_key|encryption_key)\s*[:=]\s*["'"'"'][^"'"'"']{8,}["'"'"']' \
62
+ && echo "CREDENTIAL ASSIGNMENTS FOUND" || echo "No credential assignments detected"
63
+ ```
64
+
65
+ Flag any non-test, non-example matches to the user. Matches in files under
66
+ `__tests__/`, `__mocks__/`, `*.test.*`, `*.spec.*`, or `*.example.*` may be
67
+ false positives — confirm with the user before blocking.
68
+
69
+ ### 2d — .env files not listed in .gitignore
70
+
71
+ ```bash
72
+ git diff --cached --name-only | grep -E '^\.env' | while read f; do
73
+ grep -qxF "$f" .gitignore 2>/dev/null || grep -qxF "/$f" .gitignore 2>/dev/null || \
74
+ echo "WARNING: $f is staged but not in .gitignore"
75
+ done
76
+ ```
77
+
78
+ Any `.env*` file staged that is not covered by `.gitignore` is an **automatic FAIL**.
79
+
80
+ ---
81
+
82
+ **If the secrets scan is fully clean**, output:
83
+ ```
84
+ ✓ Secrets scan — no issues found
85
+ ```
86
+ and continue to Step 3.
87
+
88
+ ## Step 3 — Lint
89
+
90
+ ```bash
91
+ time bun run lint 2>&1
92
+ ```
93
+
94
+ **If lint exits non-zero:**
95
+ 1. Run `bun run lint:fix` to auto-fix what ESLint can.
96
+ 2. Re-run `bun run lint` and capture the remaining output.
97
+ 3. For any errors that lint:fix could not resolve, read the flagged files and
98
+ fix them manually (one at a time — don't batch-guess).
99
+ 4. Re-run lint a final time to confirm exit 0.
100
+
101
+ **Common lint patterns in this project:**
102
+ - Unused imports → remove them
103
+ - `any` type → narrow to the real type or use `unknown`
104
+ - Missing return types on exported functions → add them
105
+ - React hooks dependency array warnings → fix the dependency, don't suppress
106
+
107
+ ## Step 4 — Typecheck
108
+
109
+ ```bash
110
+ time bun run typecheck 2>&1
111
+ ```
112
+
113
+ TypeScript errors are **never suppressed with `@ts-ignore` or `as any`** unless
114
+ there is a pre-existing justification comment. Fix each error properly:
115
+ - Missing property → add it to the type or interface
116
+ - Wrong type → correct the calling code
117
+ - Module not found → check the import path / tsconfig paths
118
+
119
+ Re-run typecheck after every fix to confirm the error is gone, not shifted.
120
+
121
+ ## Step 5 — Tests
122
+
123
+ ```bash
124
+ time bun run test 2>&1
125
+ ```
126
+
127
+ **If tests fail:**
128
+ 1. Read the full failure output — identify the exact test name and file.
129
+ 2. Open the test file and read the failing assertion.
130
+ 3. Open the production file it is testing and understand why the behaviour
131
+ changed.
132
+ 4. Fix the production code **or** update the test (update tests only when the
133
+ behaviour change was intentional — new feature, intentional refactor).
134
+ 5. Re-run `bun run test` to confirm all tests pass.
135
+
136
+ Never delete or skip a test just to make the suite green — diagnose and fix the
137
+ underlying issue.
138
+
139
+ ## Step 6 — Final summary
140
+
141
+ After all checks pass, output a summary block like this:
142
+
143
+ ```
144
+ ✅ Pre-commit checks passed
145
+ ─────────────────────────────────────
146
+ secrets ✓ clean
147
+ lint ✓ 1.2s
148
+ typecheck ✓ 4.7s
149
+ tests ✓ 3.6s 559 passed
150
+ ─────────────────────────────────────
151
+ Total ✓ 9.5s
152
+ Ready to commit.
153
+ ```
154
+
155
+ If any step cannot be fixed within a reasonable number of attempts (3–4 tries),
156
+ stop and surface the remaining error to the user with a clear description of
157
+ what is failing and why, rather than looping forever.
158
+
159
+ ## Scope note
160
+
161
+ This skill covers **pre-commit quality gates only**. It does not:
162
+ - Bump version numbers
163
+ - Create git commits or tags
164
+ - Push to remote
165
+
166
+ For releasing, use `bun run release` (patch), `bun run release:minor`, or
167
+ `bun run release:major` after this skill exits successfully.
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "FinanceBuddha Base",
4
+ "compilerOptions": {
5
+ "target": "ES2017",
6
+ "lib": ["esnext"],
7
+ "skipLibCheck": true,
8
+ "strict": true,
9
+ "noEmit": true,
10
+ "esModuleInterop": true,
11
+ "module": "esnext",
12
+ "moduleResolution": "bundler",
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "experimentalDecorators": true,
16
+ "emitDecoratorMetadata": true
17
+ },
18
+ "exclude": ["node_modules", "coverage"]
19
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "FinanceBuddha Next.js",
4
+ "compilerOptions": {
5
+ "target": "ES2017",
6
+ "lib": ["dom", "dom.iterable", "esnext"],
7
+ "allowJs": true,
8
+ "skipLibCheck": true,
9
+ "strict": true,
10
+ "noEmit": true,
11
+ "esModuleInterop": true,
12
+ "module": "esnext",
13
+ "moduleResolution": "bundler",
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "jsx": "react-jsx",
17
+ "incremental": true,
18
+ "experimentalDecorators": true,
19
+ "emitDecoratorMetadata": true,
20
+ "plugins": [{ "name": "next" }],
21
+ "paths": { "@/*": ["./*"] }
22
+ },
23
+ "include": [
24
+ "next-env.d.ts",
25
+ "**/*.ts",
26
+ "**/*.tsx",
27
+ ".next/types/**/*.ts",
28
+ ".next/dev/types/**/*.ts",
29
+ "**/*.mts"
30
+ ],
31
+ "exclude": ["node_modules", "scripts", "coverage"]
32
+ }