agentic-sdd-framework 1.4.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/.agents/AGENTS.template.md +59 -0
- package/.agents/CONTEXT.template.md +41 -0
- package/.agents/ENTRYPOINT.template.md +31 -0
- package/.agents/skills/ast-navigator/SKILL.md +31 -0
- package/.agents/skills/ast-navigator/adapters/ast-grep.md +18 -0
- package/.agents/skills/ast-navigator/adapters/graphify.md +19 -0
- package/.agents/skills/ast-navigator/adapters/lsp.md +16 -0
- package/.agents/skills/ast-navigator/adapters/ripgrep.md +19 -0
- package/.agents/skills/auditor-executor-protocol/SKILL.md +410 -0
- package/.agents/skills/auditor-executor-protocol/references/autonomous-mode.md +144 -0
- package/.agents/skills/auditor-executor-protocol/references/failure-modes-and-example.md +103 -0
- package/.agents/skills/auditor-executor-protocol/references/handoffs.md +133 -0
- package/.agents/skills/auditor-executor-protocol/references/tasks-and-gates.md +81 -0
- package/.agents/skills/no-ai-slop/LICENSE +21 -0
- package/.agents/skills/no-ai-slop/SKILL.md +52 -0
- package/.agents/skills/strategic-cto/SKILL.md +54 -0
- package/CHANGELOG.md +117 -0
- package/LICENSE +21 -0
- package/README.md +244 -0
- package/docs/SPEC_TEMPLATE.md +78 -0
- package/docs/decisions/ADR_TEMPLATE.md +49 -0
- package/docs/guidelines/AST_NAVIGATION.md +51 -0
- package/docs/guides/AGENT_CREDENTIALS.md +75 -0
- package/docs/guides/GITHUB_CLI_SETUP.md +74 -0
- package/docs/incidents/0000-00-00-incident-template.md +35 -0
- package/docs/roadmap/templates/compliance-log.md +37 -0
- package/docs/roadmap/templates/execution-guide.md +75 -0
- package/docs/roadmap/templates/plan-of-record.md +49 -0
- package/package.json +49 -0
- package/scripts/check-copy-slop.js +120 -0
- package/scripts/check-file-size.js +66 -0
- package/scripts/check-spec.js +201 -0
- package/scripts/check-system-prerequisites.js +133 -0
- package/scripts/check-versions.js +50 -0
- package/scripts/dev/fuzz-spec-markup.js +123 -0
- package/scripts/dev/set-npm-publish-token.sh +40 -0
- package/scripts/dev/sync-vendored.js +94 -0
- package/scripts/install-git-hooks.js +103 -0
- package/scripts/lib/cli.js +60 -0
- package/scripts/lib/config.js +111 -0
- package/scripts/lib/git.js +211 -0
- package/scripts/lib/markdown.js +46 -0
- package/scripts/lib/provision.js +323 -0
- package/scripts/lib/runner.js +70 -0
- package/scripts/lib/sdd.config.schema.json +213 -0
- package/scripts/lib/slop-patterns.js +57 -0
- package/scripts/lib/spec-markup.js +346 -0
- package/scripts/lib/spec.js +226 -0
- package/scripts/lib/state.js +107 -0
- package/scripts/lib/vendor/README.md +11 -0
- package/scripts/lib/vendor/markdown-it.LICENSE +22 -0
- package/scripts/lib/vendor/markdown-it.min.js +3 -0
- package/scripts/quality-gate.js +151 -0
- package/scripts/sdd-init.js +245 -0
- package/scripts/sdd-verify.js +176 -0
- package/scripts/verify-no-secrets.js +216 -0
- package/sdd.config.json +33 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# 🐙 GitHub CLI Setup & Zero-Trust Provisioning Guide
|
|
2
|
+
|
|
3
|
+
This guide establishes the standard Day-0 provisioning procedure for repositories built with the **Agentic SDD Framework**.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Why GitHub CLI (`gh`) over the Web UI?
|
|
8
|
+
|
|
9
|
+
Using the command-line interface (`gh`) instead of clicking through github.com provides three fundamental engineering advantages:
|
|
10
|
+
|
|
11
|
+
1. **Deterministic & Scriptable:** Ensures every repository is provisioned with identical security flags (private by default, clean `.gitignore`, standard branch).
|
|
12
|
+
2. **Eliminates URL Credential Leaks:** Cloning through `gh` uses authenticated keychain tokens or SSH agent keys, preventing hardcoded credentials in `.git/config` remote URLs.
|
|
13
|
+
3. **Automated Branch Governance:** Enables setting up branch protection rules and CI/CD secret environments directly from terminal scripts.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 2. Authentication & Least-Privilege Scopes
|
|
18
|
+
|
|
19
|
+
Before running provisioning commands, verify your local authentication state:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gh auth status
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 🔒 Security Risk: Over-Privileged Personal Access Tokens (PAT)
|
|
26
|
+
When running `gh auth login`:
|
|
27
|
+
* **Recommended Protocol:** Choose `SSH` or `HTTPS` with web-browser verification.
|
|
28
|
+
* **Minimum Scopes Required:** `'repo'`, `'read:org'`, `'workflow'`.
|
|
29
|
+
* **FORBIDDEN:** Never grant `'admin:org'` or full account administration scopes to everyday CLI development machines.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 3. Provisioning a New SDD Repository
|
|
34
|
+
|
|
35
|
+
To create and clone a clean, private repository from scratch:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# 1. Create remote private repo and clone to local disk
|
|
39
|
+
gh repo create <project-name> --private --description "Spec-Driven Development Project" --clone
|
|
40
|
+
|
|
41
|
+
# 2. Navigate into the project
|
|
42
|
+
cd <project-name>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 4. Branch Protection Rules (The First Defense)
|
|
48
|
+
|
|
49
|
+
Autonomous AI agents must **never** be permitted to execute blind force-pushes (`git push --force`) to your production branch.
|
|
50
|
+
|
|
51
|
+
### Setting up Branch Protection via CLI:
|
|
52
|
+
Protect the `main` (or `master`) branch by requiring pull requests and status checks:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Verify the default branch name
|
|
56
|
+
git branch -M main
|
|
57
|
+
|
|
58
|
+
# Enforce branch protection (requires GitHub Pro/Team for private repos or public repo)
|
|
59
|
+
gh api \
|
|
60
|
+
--method PUT \
|
|
61
|
+
"repos/:owner/:repo/branches/main/protection" \
|
|
62
|
+
-f required_status_checks='{"strict":true,"contexts":["quality-gate"]}' \
|
|
63
|
+
-f enforce_admins=true \
|
|
64
|
+
-f required_pull_request_reviews=null \
|
|
65
|
+
-f restrictions=null
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 5. Security Checklist Before First Commit
|
|
71
|
+
|
|
72
|
+
- [ ] Repository is set to **Private** (unless explicitly intended as open-source).
|
|
73
|
+
- [ ] `.gitignore` contains rules for `.env`, `.vault`, `scratch/`, and private keys.
|
|
74
|
+
- [ ] No personal access tokens or credentials are typed into terminal arguments that persist in shell history (`~/.zsh_history` or `~/.bash_history`).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Incident Post-Mortem: [YYYY-MM-DD] [Incident Title]
|
|
2
|
+
|
|
3
|
+
**Date:** [YYYY-MM-DD]
|
|
4
|
+
**Author / Investigator:** [Name]
|
|
5
|
+
**Severity:** P1 (Critical) | P2 (Major) | P3 (Minor)
|
|
6
|
+
**Status:** Resolved
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Summary and Impact
|
|
11
|
+
Briefly describe what failed, which services were affected, and how the issue was detected (e.g. automated alert, customer report, test suite).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Timeline
|
|
16
|
+
* **[Time UTC]:** Initial symptom observed.
|
|
17
|
+
* **[Time UTC]:** Investigation started; root cause identified.
|
|
18
|
+
* **[Time UTC]:** Remediation deployed.
|
|
19
|
+
* **[Time UTC]:** Verification completed; service restored.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 3. Root Cause Analysis
|
|
24
|
+
Explain the underlying mechanical, configuration, or architectural failure without speculation. State what code path or environment variable triggered the failure.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 4. Remediation and Verification
|
|
29
|
+
* **Fix Applied:** [Link to commit or description of change]
|
|
30
|
+
* **Verification Command:** [Terminal command used to reproduce and verify the fix]
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 5. Inviolable Rule Created
|
|
35
|
+
Document the permanent rule added to `.agents/AGENTS.md` to ensure AI agents and engineers cannot repeat this error.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# {{PROJECT_NAME}} — Compliance Log
|
|
2
|
+
|
|
3
|
+
> **Evidence of record.** One report and one verdict per task/gate ID. Pre-generate an
|
|
4
|
+
> empty row per ID from the execution guide so nothing gets skipped silently.
|
|
5
|
+
>
|
|
6
|
+
> Maintained by the Auditor after every verdict. Compact repeated open conditions
|
|
7
|
+
> periodically instead of only appending — see the protocol's note on log maintenance.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Status board
|
|
12
|
+
|
|
13
|
+
| ID | Delivery | Verdict | Notes |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| P0-T1 | | `PENDING` | |
|
|
16
|
+
| P0-G1 | | `PENDING` | |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Open conditions
|
|
21
|
+
|
|
22
|
+
None yet.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Reports
|
|
27
|
+
|
|
28
|
+
### P0-T1 — PENDING
|
|
29
|
+
**Changed:**
|
|
30
|
+
**Verify output:**
|
|
31
|
+
**Observations:**
|
|
32
|
+
**Decisions made without stopping:**
|
|
33
|
+
|
|
34
|
+
### P0-G1 — PENDING
|
|
35
|
+
**Verify output:**
|
|
36
|
+
**Observations:**
|
|
37
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# {{PROJECT_NAME}} — Execution Guide
|
|
2
|
+
|
|
3
|
+
> **You are the Executor.** The Auditor wrote this file and will re-run every gate
|
|
4
|
+
> independently. Read `plan-of-record.md` once for context, then work only from here.
|
|
5
|
+
> Report every task in `compliance-log.md`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Rules of engagement
|
|
10
|
+
|
|
11
|
+
1. **One task at a time, in order.** No batching. Do not start a phase whose
|
|
12
|
+
predecessor is not marked `APPROVED` in the compliance log.
|
|
13
|
+
2. **Never invent a value the codebase declares.** Read it. Needing to ask for one
|
|
14
|
+
means a file was skipped.
|
|
15
|
+
3. **Never guess a cause. Observe it.** Read the log, run the query, print the value.
|
|
16
|
+
4. **Verification is running the thing, not reading the code.** "I reviewed it and it
|
|
17
|
+
looks correct" is reported as `FAILED`.
|
|
18
|
+
5. **Scope is the task text.** Unrelated problems go in *Observations*. Do not fix
|
|
19
|
+
them.
|
|
20
|
+
6. **Stop and ask** when this document contradicts the code, when a task needs a
|
|
21
|
+
decision this document does not make, or when a previously passing check starts
|
|
22
|
+
failing for reasons unrelated to your change. If you resolve an ambiguity without
|
|
23
|
+
stopping, log the decision. A decision in the plan of record is settled; a factual
|
|
24
|
+
claim never is — if this document states something the code contradicts, the
|
|
25
|
+
evidence outranks the order.
|
|
26
|
+
7. **When the system cannot do what a task asks, that is the deliverable.** Report it
|
|
27
|
+
and stop. A task that ends in a well-argued finding is a success.
|
|
28
|
+
8. Project-specific rules that apply to every task — list them here, cited from their
|
|
29
|
+
source rather than restated:
|
|
30
|
+
-
|
|
31
|
+
-
|
|
32
|
+
|
|
33
|
+
## Reporting format
|
|
34
|
+
|
|
35
|
+
For each task, append to `compliance-log.md`:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
### <TASK-ID> — DONE | BLOCKED | FAILED
|
|
39
|
+
**Changed:** <paths>
|
|
40
|
+
**Verify output:**
|
|
41
|
+
<pasted, literal, unedited command output>
|
|
42
|
+
**Observations:** <or "none">
|
|
43
|
+
**Decisions made without stopping:** <or "none">
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
A `DONE` with no pasted output is recorded as `FAILED`.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
# Phase 0 — <name>
|
|
51
|
+
|
|
52
|
+
### P0-T1 — <imperative, one line>
|
|
53
|
+
|
|
54
|
+
**Goal:**
|
|
55
|
+
**Files:**
|
|
56
|
+
**Steps:**
|
|
57
|
+
1.
|
|
58
|
+
|
|
59
|
+
**Expected failure to avoid:**
|
|
60
|
+
|
|
61
|
+
**Verify:**
|
|
62
|
+
```bash
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Report:** `P0-T1`
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### P0-G1 — Gate: <claim under test>
|
|
70
|
+
|
|
71
|
+
**Proven by:** <the check that proves the claim>
|
|
72
|
+
|
|
73
|
+
**Negative control:** <how you remove the protection, which check fails, and that it passes again once restored>
|
|
74
|
+
|
|
75
|
+
**Report:** `P0-G1`
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# {{PROJECT_NAME}} — Plan of Record
|
|
2
|
+
|
|
3
|
+
> **Status:** draft. This document holds the *what* and the *why*. Nobody implements
|
|
4
|
+
> from this file. The executable instructions live in `execution-guide.md`; evidence
|
|
5
|
+
> goes in `compliance-log.md`.
|
|
6
|
+
|
|
7
|
+
**Target version / milestone:**
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Why this exists
|
|
12
|
+
|
|
13
|
+
What problem is this solving, and why now. If several requests trace back to one
|
|
14
|
+
underlying gap, name the gap here rather than listing the requests as unrelated.
|
|
15
|
+
|
|
16
|
+
## 2. What the audit of the current system found
|
|
17
|
+
|
|
18
|
+
| Finding | Evidence |
|
|
19
|
+
|---|---|
|
|
20
|
+
| | |
|
|
21
|
+
|
|
22
|
+
State each finding with the command or artifact that established it, not from memory.
|
|
23
|
+
|
|
24
|
+
## 3. The decisions (settled — reopening one is a stop, not a choice)
|
|
25
|
+
|
|
26
|
+
### D1 — <decision name>
|
|
27
|
+
|
|
28
|
+
What was decided, what alternative was rejected and why, what failure this prevents.
|
|
29
|
+
|
|
30
|
+
## 4. What this deliberately does not do
|
|
31
|
+
|
|
32
|
+
Scope boundaries the Auditor is holding, so an Executor (or a future Auditor) doesn't
|
|
33
|
+
drift into rebuilding something that was cut on purpose.
|
|
34
|
+
|
|
35
|
+
## 5. Phases
|
|
36
|
+
|
|
37
|
+
| Phase | Delivers | Risk |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| | | |
|
|
40
|
+
|
|
41
|
+
## 6. Request traceability
|
|
42
|
+
|
|
43
|
+
| Origin request | Where it lands |
|
|
44
|
+
|---|---|
|
|
45
|
+
| | |
|
|
46
|
+
|
|
47
|
+
## 7. Standards this work is held to
|
|
48
|
+
|
|
49
|
+
Cite the project's own conventions rather than restating them here.
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agentic-sdd-framework",
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "Spec-Driven Development governance for AI coding agents: auto-loaded agent rules, specifications with recorded evidence, and a pre-push quality gate",
|
|
5
|
+
"bin": {
|
|
6
|
+
"sdd-init": "scripts/sdd-init.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"scripts/",
|
|
10
|
+
".agents/",
|
|
11
|
+
"docs/",
|
|
12
|
+
"sdd.config.json",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"CHANGELOG.md"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "node --test",
|
|
22
|
+
"init": "node scripts/sdd-init.js",
|
|
23
|
+
"verify": "node scripts/sdd-verify.js",
|
|
24
|
+
"check:prereqs": "node scripts/check-system-prerequisites.js",
|
|
25
|
+
"check:secrets": "node scripts/verify-no-secrets.js",
|
|
26
|
+
"check:slop": "node scripts/check-copy-slop.js",
|
|
27
|
+
"check:size": "node scripts/check-file-size.js",
|
|
28
|
+
"check:spec": "node scripts/check-spec.js",
|
|
29
|
+
"check:versions": "node scripts/check-versions.js",
|
|
30
|
+
"install-hooks": "node scripts/install-git-hooks.js",
|
|
31
|
+
"quality-gate": "node scripts/quality-gate.js"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"sdd",
|
|
35
|
+
"spec-driven-development",
|
|
36
|
+
"ai-agents",
|
|
37
|
+
"claude-code",
|
|
38
|
+
"antigravity",
|
|
39
|
+
"cursor",
|
|
40
|
+
"governance",
|
|
41
|
+
"auditor-executor"
|
|
42
|
+
],
|
|
43
|
+
"author": "tBeltty",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/tBeltty/agentic-sdd-framework.git"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/check-copy-slop.js
|
|
5
|
+
*
|
|
6
|
+
* Linter for AI-generated writing cliches, buzzwords, and banned patterns in prose
|
|
7
|
+
* (.md, .mdx, .txt). Code blocks, inline code and table rows are not checked.
|
|
8
|
+
* Inspired by https://github.com/petergyang/no-ai-slop (MIT License).
|
|
9
|
+
*
|
|
10
|
+
* Config (sdd.config.json -> capabilities.noAiSlop):
|
|
11
|
+
* enabled false skips the check (default true)
|
|
12
|
+
* exclude path prefixes to skip
|
|
13
|
+
* maxEmDashes em dashes allowed per file, counted individually (default 1)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { listFiles, readFiles, isBinary, WORKTREE } = require('./lib/git');
|
|
17
|
+
const { loadConfig, getIn } = require('./lib/config');
|
|
18
|
+
const { runCheckCli } = require('./lib/cli');
|
|
19
|
+
const { normalizeEol, createFenceTracker } = require('./lib/markdown');
|
|
20
|
+
const { BANNED_PATTERNS } = require('./lib/slop-patterns');
|
|
21
|
+
|
|
22
|
+
const PROSE_EXTENSIONS = ['.md', '.mdx', '.txt'];
|
|
23
|
+
|
|
24
|
+
// The skill that defines the banned patterns must quote them; vendored skills and the
|
|
25
|
+
// Rigor templates are synced from their upstream repositories and linted there. Rigor
|
|
26
|
+
// documents (specification.roadmapDir) are records in the protocol's format and hold
|
|
27
|
+
// pasted command output.
|
|
28
|
+
// sdd-init copies the skills into .claude/skills/ where symlinks are unavailable (Windows
|
|
29
|
+
// without Developer Mode), so those copies are excluded too.
|
|
30
|
+
const DEFAULT_EXCLUDE = [
|
|
31
|
+
'.agents/skills/no-ai-slop/',
|
|
32
|
+
'.agents/skills/auditor-executor-protocol/',
|
|
33
|
+
'.claude/skills/no-ai-slop/',
|
|
34
|
+
'.claude/skills/auditor-executor-protocol/',
|
|
35
|
+
'docs/roadmap/templates/'
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
function lintContent(content, { maxEmDashes = 1 } = {}) {
|
|
39
|
+
const violations = [];
|
|
40
|
+
const emDashLines = [];
|
|
41
|
+
let emDashes = 0;
|
|
42
|
+
const fence = createFenceTracker();
|
|
43
|
+
|
|
44
|
+
normalizeEol(content).split('\n').forEach((rawLine, index) => {
|
|
45
|
+
const trimmed = rawLine.trim();
|
|
46
|
+
if (fence.update(rawLine) || fence.inside) return;
|
|
47
|
+
if (trimmed.startsWith('|') || /^-{3,}$/.test(trimmed)) return;
|
|
48
|
+
|
|
49
|
+
const line = rawLine.replace(/`[^`]*`/g, '``');
|
|
50
|
+
const dashes = (line.match(/—/g) || []).length;
|
|
51
|
+
if (dashes > 0) {
|
|
52
|
+
emDashes += dashes;
|
|
53
|
+
emDashLines.push(index + 1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const pattern of BANNED_PATTERNS) {
|
|
57
|
+
const match = line.match(pattern.regex);
|
|
58
|
+
if (match) {
|
|
59
|
+
violations.push({
|
|
60
|
+
line: index + 1,
|
|
61
|
+
pattern: pattern.name,
|
|
62
|
+
matchedText: match[0].trim(),
|
|
63
|
+
lineText: trimmed.slice(0, 100)
|
|
64
|
+
});
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (emDashes > maxEmDashes) {
|
|
71
|
+
violations.push({
|
|
72
|
+
line: emDashLines[0],
|
|
73
|
+
pattern: `Em Dash Overuse (${emDashes} found, max ${maxEmDashes} per file)`,
|
|
74
|
+
matchedText: '—',
|
|
75
|
+
lineText: `lines ${emDashLines.join(', ')}`
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return violations;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function run({ root, source = WORKTREE } = {}) {
|
|
82
|
+
const config = loadConfig(root, source);
|
|
83
|
+
if (getIn(config, 'capabilities.noAiSlop.enabled', true) === false) {
|
|
84
|
+
return { ok: true, report: '⏭️ No-AI-Slop linter disabled in sdd.config.json.' };
|
|
85
|
+
}
|
|
86
|
+
const roadmapDir = `${getIn(config, 'specification.roadmapDir', 'docs/roadmap').replace(/\/+$/, '')}/`;
|
|
87
|
+
const exclude = [...DEFAULT_EXCLUDE, roadmapDir, ...getIn(config, 'capabilities.noAiSlop.exclude', [])];
|
|
88
|
+
const maxEmDashes = getIn(config, 'capabilities.noAiSlop.maxEmDashes', 1);
|
|
89
|
+
|
|
90
|
+
const files = listFiles(root, source)
|
|
91
|
+
.filter(f => PROSE_EXTENSIONS.some(ext => f.endsWith(ext)))
|
|
92
|
+
.filter(f => !exclude.some(prefix => f.startsWith(prefix)));
|
|
93
|
+
|
|
94
|
+
const violations = [];
|
|
95
|
+
for (const [file, buffer] of readFiles(root, files, source)) {
|
|
96
|
+
if (!buffer || isBinary(buffer)) continue;
|
|
97
|
+
for (const v of lintContent(buffer.toString('utf8'), { maxEmDashes })) {
|
|
98
|
+
violations.push({ file, ...v });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (violations.length === 0) {
|
|
103
|
+
return { ok: true, report: `✅ Scanned ${files.length} file(s). Zero AI slop patterns detected.` };
|
|
104
|
+
}
|
|
105
|
+
const lines = [`❌ Found ${violations.length} AI writing pattern violation(s):\n`];
|
|
106
|
+
for (const v of violations) {
|
|
107
|
+
lines.push(` - File: ${v.file}:${v.line}`);
|
|
108
|
+
lines.push(` Pattern: ${v.pattern}`);
|
|
109
|
+
lines.push(` Matched: "${v.matchedText}"`);
|
|
110
|
+
lines.push(` Context: ${v.lineText}\n`);
|
|
111
|
+
}
|
|
112
|
+
lines.push('Action: Rewrite directly without empty filler or binary contrasts.');
|
|
113
|
+
return { ok: false, report: lines.join('\n') };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (require.main === module) {
|
|
117
|
+
runCheckCli('✍️ Agentic SDD Framework: No-AI-Slop Copy Linter', run);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = { lintContent, run };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* scripts/check-file-size.js
|
|
5
|
+
*
|
|
6
|
+
* Enforces architecture.maxLocPerFile from sdd.config.json on tracked source files.
|
|
7
|
+
* Documentation, data and lockfiles are not counted.
|
|
8
|
+
*
|
|
9
|
+
* Config (sdd.config.json -> architecture):
|
|
10
|
+
* maxLocPerFile line limit per source file (default 400; 0 disables the check)
|
|
11
|
+
* maxLocExclude path prefixes to skip (for example generated code)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { listFiles, readFiles, isBinary, WORKTREE } = require('./lib/git');
|
|
16
|
+
const { loadConfig, getIn } = require('./lib/config');
|
|
17
|
+
const { runCheckCli } = require('./lib/cli');
|
|
18
|
+
|
|
19
|
+
const SOURCE_EXTENSIONS = new Set([
|
|
20
|
+
'.js', '.mjs', '.cjs', '.jsx', '.ts', '.mts', '.cts', '.tsx', '.vue', '.svelte',
|
|
21
|
+
'.py', '.go', '.rs', '.java', '.kt', '.kts', '.scala', '.rb', '.php', '.cs',
|
|
22
|
+
'.c', '.h', '.cc', '.cpp', '.hpp', '.m', '.swift', '.dart', '.ex', '.exs',
|
|
23
|
+
'.sh', '.bash', '.zsh', '.lua', '.sql'
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
function countLines(text) {
|
|
27
|
+
if (text.length === 0) return 0;
|
|
28
|
+
const normalized = text.replace(/\r\n?/g, '\n');
|
|
29
|
+
return normalized.split('\n').length - (normalized.endsWith('\n') ? 1 : 0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function run({ root, source = WORKTREE } = {}) {
|
|
33
|
+
const config = loadConfig(root, source);
|
|
34
|
+
const limit = getIn(config, 'architecture.maxLocPerFile', 400);
|
|
35
|
+
if (!limit) {
|
|
36
|
+
return { ok: true, report: '⏭️ File size limit disabled (architecture.maxLocPerFile is 0).' };
|
|
37
|
+
}
|
|
38
|
+
const exclude = getIn(config, 'architecture.maxLocExclude', []);
|
|
39
|
+
|
|
40
|
+
const files = listFiles(root, source)
|
|
41
|
+
.filter(f => SOURCE_EXTENSIONS.has(path.extname(f).toLowerCase()))
|
|
42
|
+
.filter(f => !exclude.some(prefix => f.startsWith(prefix)));
|
|
43
|
+
|
|
44
|
+
const oversized = [];
|
|
45
|
+
for (const [file, buffer] of readFiles(root, files, source)) {
|
|
46
|
+
if (!buffer || isBinary(buffer)) continue;
|
|
47
|
+
const lines = countLines(buffer.toString('utf8'));
|
|
48
|
+
if (lines > limit) oversized.push({ file, lines });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (oversized.length === 0) {
|
|
52
|
+
return { ok: true, report: `✅ ${files.length} source file(s) within ${limit} lines.` };
|
|
53
|
+
}
|
|
54
|
+
const report = [`❌ ${oversized.length} source file(s) exceed architecture.maxLocPerFile (${limit}):\n`];
|
|
55
|
+
for (const { file, lines } of oversized.sort((a, b) => b.lines - a.lines)) {
|
|
56
|
+
report.push(` - ${file}: ${lines} lines`);
|
|
57
|
+
}
|
|
58
|
+
report.push('\nAction: Split the file by responsibility, or add generated paths to architecture.maxLocExclude.');
|
|
59
|
+
return { ok: false, report: report.join('\n') };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (require.main === module) {
|
|
63
|
+
runCheckCli('📏 Agentic SDD Framework: File Size Limit', run);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { countLines, run };
|