Skip to content

Commit dd7e3fe

Browse files
Isak Ramstedtclaude
andcommitted
chore: bootstrap projektgrund (spec-kit, constitution, CI/CD)
Sätter upp Faktura — multi-tenant SaaS-fakturasystem — spec-driven som VMPage med konventioner från SwingBy. - SPEC-BRIEF.md: produktbeslut (delad DB + tenantId, egen JWT + roller, rate limiting per tenant, Stripe testläge/prenumeration, deploy som VMPage) - constitution.md v1.0.0: principer I–VI (spec-driven, Clean Code/SOLID, TDD, API-kontrakt först, multi-tenant isolering & säkerhet, CI/CD-gate) - CLAUDE.md, README.md, .gitignore (secrets gitignorerade) - GitHub Actions: ci.yml (build/test/lint, toleranta tills koden landar) + deploy.yml (Cloudflare Pages + Render, gated på secrets) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent da44b18 commit dd7e3fe

34 files changed

Lines changed: 3073 additions & 38 deletions
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
name: speckit-git-commit
3+
description: Auto-commit changes after a Spec Kit command completes
4+
compatibility: Requires spec-kit project structure with .specify/ directory
5+
metadata:
6+
author: github-spec-kit
7+
source: git:commands/speckit.git.commit.md
8+
---
9+
10+
# Auto-Commit Changes
11+
12+
Automatically stage and commit all changes after a Spec Kit command completes.
13+
14+
## Behavior
15+
16+
This command is invoked as a hook after (or before) core commands. It:
17+
18+
1. Determines the event name from the hook context (e.g., if invoked as an `after_specify` hook, the event is `after_specify`; if `before_plan`, the event is `before_plan`)
19+
2. Checks `.specify/extensions/git/git-config.yml` for the `auto_commit` section
20+
3. Looks up the specific event key to see if auto-commit is enabled
21+
4. Falls back to `auto_commit.default` if no event-specific key exists
22+
5. Uses the per-command `message` if configured, otherwise a default message
23+
6. If enabled and there are uncommitted changes, runs `git add .` + `git commit`
24+
25+
## Execution
26+
27+
Determine the event name from the hook that triggered this command, then run the script:
28+
29+
- **Bash**: `.specify/extensions/git/scripts/bash/auto-commit.sh <event_name>`
30+
- **PowerShell**: `.specify/extensions/git/scripts/powershell/auto-commit.ps1 <event_name>`
31+
32+
Replace `<event_name>` with the actual hook event (e.g., `after_specify`, `before_plan`, `after_implement`).
33+
34+
## Configuration
35+
36+
In `.specify/extensions/git/git-config.yml`:
37+
38+
```yaml
39+
auto_commit:
40+
default: false # Global toggle — set true to enable for all commands
41+
after_specify:
42+
enabled: true # Override per-command
43+
message: "[Spec Kit] Add specification"
44+
after_plan:
45+
enabled: false
46+
message: "[Spec Kit] Add implementation plan"
47+
```
48+
49+
## Graceful Degradation
50+
51+
- If Git is not available or the current directory is not a repository: skips with a warning
52+
- If no config file exists: skips (disabled by default)
53+
- If no changes to commit: skips with a message
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
---
2+
name: speckit-git-feature
3+
description: Create a feature branch with sequential or timestamp numbering
4+
compatibility: Requires spec-kit project structure with .specify/ directory
5+
metadata:
6+
author: github-spec-kit
7+
source: git:commands/speckit.git.feature.md
8+
---
9+
10+
# Create Feature Branch
11+
12+
Create and switch to a new git feature branch for the given specification. This command handles **branch creation only** — the spec directory and files are created by the core `/speckit.specify` workflow.
13+
14+
## User Input
15+
16+
```text
17+
$ARGUMENTS
18+
```
19+
20+
You **MUST** consider the user input before proceeding (if not empty).
21+
22+
## Environment Variable Override
23+
24+
If the user explicitly provided `GIT_BRANCH_NAME` (e.g., via environment variable, argument, or in their request), pass it through to the script by setting the `GIT_BRANCH_NAME` environment variable before invoking the script. When `GIT_BRANCH_NAME` is set:
25+
- The script uses the exact value as the branch name, bypassing all prefix/suffix generation
26+
- `--short-name`, `--number`, and `--timestamp` flags are ignored
27+
- `FEATURE_NUM` is extracted from the name if it starts with a numeric prefix, otherwise set to the full branch name
28+
29+
## Prerequisites
30+
31+
- Verify Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
32+
- If Git is not available, warn the user and skip branch creation
33+
34+
## Branch Numbering Mode
35+
36+
Determine the branch numbering strategy by checking configuration in this order:
37+
38+
1. Check `.specify/extensions/git/git-config.yml` for `branch_numbering` value
39+
2. Check `.specify/init-options.json` for `branch_numbering` value (backward compatibility)
40+
3. Default to `sequential` if neither exists
41+
42+
## Execution
43+
44+
Generate a concise short name (2-4 words) for the branch:
45+
- Analyze the feature description and extract the most meaningful keywords
46+
- Use action-noun format when possible (e.g., "add-user-auth", "fix-payment-bug")
47+
- Preserve technical terms and acronyms (OAuth2, API, JWT, etc.)
48+
49+
Run the appropriate script based on your platform:
50+
51+
- **Bash**: `.specify/extensions/git/scripts/bash/create-new-feature.sh --json --short-name "<short-name>" "<feature description>"`
52+
- **Bash (timestamp)**: `.specify/extensions/git/scripts/bash/create-new-feature.sh --json --timestamp --short-name "<short-name>" "<feature description>"`
53+
- **PowerShell**: `.specify/extensions/git/scripts/powershell/create-new-feature.ps1 -Json -ShortName "<short-name>" "<feature description>"`
54+
- **PowerShell (timestamp)**: `.specify/extensions/git/scripts/powershell/create-new-feature.ps1 -Json -Timestamp -ShortName "<short-name>" "<feature description>"`
55+
56+
**IMPORTANT**:
57+
- Do NOT pass `--number` — the script determines the correct next number automatically
58+
- Always include the JSON flag (`--json` for Bash, `-Json` for PowerShell) so the output can be parsed reliably
59+
- You must only ever run this script once per feature
60+
- The JSON output will contain `BRANCH_NAME` and `FEATURE_NUM`
61+
62+
## Graceful Degradation
63+
64+
If Git is not installed or the current directory is not a Git repository:
65+
- Branch creation is skipped with a warning: `[specify] Warning: Git repository not detected; skipped branch creation`
66+
- The script still outputs `BRANCH_NAME` and `FEATURE_NUM` so the caller can reference them
67+
68+
## Output
69+
70+
The script outputs JSON with:
71+
- `BRANCH_NAME`: The branch name (e.g., `003-user-auth` or `20260319-143022-user-auth`)
72+
- `FEATURE_NUM`: The numeric or timestamp prefix used
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
name: speckit-git-initialize
3+
description: Initialize a Git repository with an initial commit
4+
compatibility: Requires spec-kit project structure with .specify/ directory
5+
metadata:
6+
author: github-spec-kit
7+
source: git:commands/speckit.git.initialize.md
8+
---
9+
10+
# Initialize Git Repository
11+
12+
Initialize a Git repository in the current project directory if one does not already exist.
13+
14+
## Execution
15+
16+
Run the appropriate script from the project root:
17+
18+
- **Bash**: `.specify/extensions/git/scripts/bash/initialize-repo.sh`
19+
- **PowerShell**: `.specify/extensions/git/scripts/powershell/initialize-repo.ps1`
20+
21+
If the extension scripts are not found, fall back to:
22+
- **Bash**: `git init && git add . && git commit -m "Initial commit from Specify template"`
23+
- **PowerShell**: `git init; git add .; git commit -m "Initial commit from Specify template"`
24+
25+
The script handles all checks internally:
26+
- Skips if Git is not available
27+
- Skips if already inside a Git repository
28+
- Runs `git init`, `git add .`, and `git commit` with an initial commit message
29+
30+
## Customization
31+
32+
Replace the script to add project-specific Git initialization steps:
33+
- Custom `.gitignore` templates
34+
- Default branch naming (`git config init.defaultBranch`)
35+
- Git LFS setup
36+
- Git hooks installation
37+
- Commit signing configuration
38+
- Git Flow initialization
39+
40+
## Output
41+
42+
On success:
43+
- `✓ Git repository initialized`
44+
45+
## Graceful Degradation
46+
47+
If Git is not installed:
48+
- Warn the user
49+
- Skip repository initialization
50+
- The project continues to function without Git (specs can still be created under `specs/`)
51+
52+
If Git is installed but `git init`, `git add .`, or `git commit` fails:
53+
- Surface the error to the user
54+
- Stop this command rather than continuing with a partially initialized repository
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
name: speckit-git-remote
3+
description: Detect Git remote URL for GitHub integration
4+
compatibility: Requires spec-kit project structure with .specify/ directory
5+
metadata:
6+
author: github-spec-kit
7+
source: git:commands/speckit.git.remote.md
8+
---
9+
10+
# Detect Git Remote URL
11+
12+
Detect the Git remote URL for integration with GitHub services (e.g., issue creation).
13+
14+
## Prerequisites
15+
16+
- Check if Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
17+
- If Git is not available, output a warning and return empty:
18+
```
19+
[specify] Warning: Git repository not detected; cannot determine remote URL
20+
```
21+
22+
## Execution
23+
24+
Run the following command to get the remote URL:
25+
26+
```bash
27+
git config --get remote.origin.url
28+
```
29+
30+
## Output
31+
32+
Parse the remote URL and determine:
33+
34+
1. **Repository owner**: Extract from the URL (e.g., `github` from `https://github.com/github/spec-kit.git`)
35+
2. **Repository name**: Extract from the URL (e.g., `spec-kit` from `https://github.com/github/spec-kit.git`)
36+
3. **Is GitHub**: Whether the remote points to a GitHub repository
37+
38+
Supported URL formats:
39+
- HTTPS: `https://github.com/<owner>/<repo>.git`
40+
- SSH: `git@github.com:<owner>/<repo>.git`
41+
42+
> [!CAUTION]
43+
> ONLY report a GitHub repository if the remote URL actually points to github.com.
44+
> Do NOT assume the remote is GitHub if the URL format doesn't match.
45+
46+
## Graceful Degradation
47+
48+
If Git is not installed, the directory is not a Git repository, or no remote is configured:
49+
- Return an empty result
50+
- Do NOT error — other workflows should continue without Git remote information
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
name: speckit-git-validate
3+
description: Validate current branch follows feature branch naming conventions
4+
compatibility: Requires spec-kit project structure with .specify/ directory
5+
metadata:
6+
author: github-spec-kit
7+
source: git:commands/speckit.git.validate.md
8+
---
9+
10+
# Validate Feature Branch
11+
12+
Validate that the current Git branch follows the expected feature branch naming conventions.
13+
14+
## Prerequisites
15+
16+
- Check if Git is available by running `git rev-parse --is-inside-work-tree 2>/dev/null`
17+
- If Git is not available, output a warning and skip validation:
18+
```
19+
[specify] Warning: Git repository not detected; skipped branch validation
20+
```
21+
22+
## Validation Rules
23+
24+
Get the current branch name:
25+
26+
```bash
27+
git rev-parse --abbrev-ref HEAD
28+
```
29+
30+
The branch name must match one of these patterns:
31+
32+
1. **Sequential**: `^[0-9]{3,}-` (e.g., `001-feature-name`, `042-fix-bug`, `1000-big-feature`)
33+
2. **Timestamp**: `^[0-9]{8}-[0-9]{6}-` (e.g., `20260319-143022-feature-name`)
34+
35+
## Execution
36+
37+
If on a feature branch (matches either pattern):
38+
- Output: `✓ On feature branch: <branch-name>`
39+
- Check if the corresponding spec directory exists under `specs/`:
40+
- For sequential branches, look for `specs/<prefix>-*` where prefix matches the numeric portion
41+
- For timestamp branches, look for `specs/<prefix>-*` where prefix matches the `YYYYMMDD-HHMMSS` portion
42+
- If spec directory exists: `✓ Spec directory found: <path>`
43+
- If spec directory missing: `⚠ No spec directory found for prefix <prefix>`
44+
45+
If NOT on a feature branch:
46+
- Output: `✗ Not on a feature branch. Current branch: <branch-name>`
47+
- Output: `Feature branches should be named like: 001-feature-name or 20260319-143022-feature-name`
48+
49+
## Graceful Degradation
50+
51+
If Git is not installed or the directory is not a Git repository:
52+
- Check the `SPECIFY_FEATURE` environment variable as a fallback
53+
- If set, validate that value against the naming patterns
54+
- If not set, skip validation with a warning

.github/workflows/ci.yml

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
name: CI
2+
3+
# Build + test + lint på varje PR och push till huvudbranchar.
4+
# Jobben är toleranta: tills backend/ resp. frontend/ finns hoppar de över
5+
# build/test så grund-commiten är grön. När koden landar blir de en riktig gate.
6+
on:
7+
push:
8+
branches: [main, develop]
9+
pull_request:
10+
branches: [main, develop]
11+
12+
permissions:
13+
contents: read
14+
15+
concurrency:
16+
group: ci-${{ github.ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
backend:
21+
name: Backend (.NET)
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v4
25+
26+
- name: Hitta .NET-lösning
27+
id: detect
28+
run: |
29+
if ls backend/*.sln backend/**/*.csproj >/dev/null 2>&1; then
30+
echo "exists=true" >> "$GITHUB_OUTPUT"
31+
else
32+
echo "exists=false" >> "$GITHUB_OUTPUT"
33+
echo "::notice::Ingen backend ännu — hoppar över .NET-build/test."
34+
fi
35+
36+
- name: Setup .NET 10
37+
if: steps.detect.outputs.exists == 'true'
38+
uses: actions/setup-dotnet@v4
39+
with:
40+
dotnet-version: '10.0.x'
41+
42+
- name: Restore
43+
if: steps.detect.outputs.exists == 'true'
44+
working-directory: backend
45+
run: dotnet restore
46+
47+
- name: Build
48+
if: steps.detect.outputs.exists == 'true'
49+
working-directory: backend
50+
run: dotnet build --no-restore --configuration Release
51+
52+
- name: Test
53+
if: steps.detect.outputs.exists == 'true'
54+
working-directory: backend
55+
run: dotnet test --no-build --configuration Release --verbosity normal
56+
57+
frontend:
58+
name: Frontend (React/Vite)
59+
runs-on: ubuntu-latest
60+
steps:
61+
- uses: actions/checkout@v4
62+
63+
- name: Hitta frontend
64+
id: detect
65+
run: |
66+
if [ -f frontend/package.json ]; then
67+
echo "exists=true" >> "$GITHUB_OUTPUT"
68+
else
69+
echo "exists=false" >> "$GITHUB_OUTPUT"
70+
echo "::notice::Ingen frontend ännu — hoppar över npm-build/test."
71+
fi
72+
73+
- name: Setup Node
74+
if: steps.detect.outputs.exists == 'true'
75+
uses: actions/setup-node@v4
76+
with:
77+
node-version: '20'
78+
cache: npm
79+
cache-dependency-path: frontend/package-lock.json
80+
81+
- name: Install
82+
if: steps.detect.outputs.exists == 'true'
83+
working-directory: frontend
84+
run: npm ci
85+
86+
- name: Lint
87+
if: steps.detect.outputs.exists == 'true'
88+
working-directory: frontend
89+
run: npm run lint --if-present
90+
91+
- name: Test
92+
if: steps.detect.outputs.exists == 'true'
93+
working-directory: frontend
94+
run: npm test --if-present
95+
96+
- name: Build
97+
if: steps.detect.outputs.exists == 'true'
98+
working-directory: frontend
99+
run: npm run build --if-present

0 commit comments

Comments
 (0)