syntropic 0.8.0 → 0.9.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/README.md +63 -78
- package/bin/syntropic.js +4 -0
- package/commands/audit.js +293 -0
- package/commands/init.js +99 -14
- package/commands/remove.js +220 -0
- package/commands/report.js +52 -1
- package/package.json +5 -2
- package/templates/CLAUDE.md +24 -0
- package/templates/agents-md.template +13 -0
- package/templates/claude-append.template +10 -0
- package/templates/copilot-instructions.template +11 -0
- package/templates/cursorrules.template +13 -0
- package/templates/docs/BACKLOG.template +40 -0
- package/templates/docs/ISSUES.template +51 -0
- package/templates/docs/NORTH_STAR.template +73 -0
- package/templates/docs/adr/000-TEMPLATE.template +39 -0
- package/templates/tool-append.template +10 -0
- package/templates/windsurfrules.template +13 -0
package/README.md
CHANGED
|
@@ -1,127 +1,112 @@
|
|
|
1
1
|
# syntropic
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Ship better software. Prove it on your own code first.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A development methodology for AI-assisted coding. Works with **Claude Code**, **Cursor**, **Windsurf**, **GitHub Copilot**, and **OpenAI Codex**.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Try Before You Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx syntropic
|
|
10
|
+
npx syntropic audit
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Analyses your last 20 git commits for process issues — localhost references, env files in git, commit discipline, fix ratios, governance docs. No install needed. Nothing sent anywhere. See what the methodology catches on **your** code.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
14
16
|
|
|
15
17
|
```bash
|
|
16
|
-
cd my-project
|
|
17
18
|
npx syntropic init
|
|
18
19
|
```
|
|
19
20
|
|
|
20
|
-
You
|
|
21
|
-
|
|
22
|
-
| Tool | File Generated |
|
|
23
|
-
|------|---------------|
|
|
24
|
-
| Claude Code | `CLAUDE.md` |
|
|
25
|
-
| Cursor | `.cursorrules` |
|
|
26
|
-
| Windsurf | `.windsurfrules` |
|
|
27
|
-
| GitHub Copilot | `.github/copilot-instructions.md` |
|
|
28
|
-
| OpenAI Codex | `AGENTS.md` |
|
|
29
|
-
|
|
30
|
-
## How It Works
|
|
31
|
-
|
|
32
|
-
**Server-served, client-computed.** Your instruction file contains a single bootstrap rule (EG13) that tells your AI tool to fetch the full methodology from the Syntropic server at the start of every development cycle. All rules, agents, and learning data are served fresh — always up to date, zero local files to maintain.
|
|
33
|
-
|
|
34
|
-
Your LLM does all the compute. Syntropic serves the methodology but runs zero inference. Your data stays on your machine.
|
|
21
|
+
You get: a disciplined development pipeline, governance doc templates, and a connection to the PRISM methodology network — so your rules stay current as the methodology evolves.
|
|
35
22
|
|
|
36
23
|
## What You Get
|
|
37
24
|
|
|
38
|
-
**Evergreen Rules
|
|
25
|
+
**Evergreen Rules** — battle-tested development rules fetched fresh at every cycle:
|
|
39
26
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
27
|
+
| Rule | What it prevents | Real-world save |
|
|
28
|
+
|------|-----------------|-----------------|
|
|
29
|
+
| **EG1: Pre-flight** | Localhost refs, broken builds reaching production | Caught 3 localhost references pre-deploy |
|
|
30
|
+
| **EG7: Pipeline** | AI jumping to code without research or planning | Structured 123 features across Full/Lightweight/Minimum cycles |
|
|
31
|
+
| **EG8: Test first** | Untested changes reaching production users | Every fix caught on test page — 12 verified promotions |
|
|
32
|
+
| **EG10: Env hygiene** | Env var corruption (trailing newlines, wrong formats) | Prevented silent API failures across 6 providers |
|
|
33
|
+
| **EG11: Prod sync** | Access control divergence between test and production | Caught lockout bug before 8+ users were affected |
|
|
34
|
+
| **EG14: Doc discipline** | Lost decisions, repeated mistakes, no project memory | Backlog, issues, and ADRs updated every cycle |
|
|
47
35
|
|
|
48
|
-
**
|
|
36
|
+
**Governance Docs** — project management templates your AI assistant reads before every cycle:
|
|
49
37
|
|
|
50
|
-
|
|
38
|
+
```
|
|
39
|
+
.claude/docs/NORTH_STAR.md Your vision, goals, and design principles
|
|
40
|
+
.claude/docs/BACKLOG.md Prioritised work with status tracking
|
|
41
|
+
.claude/docs/ISSUES.md Bug log and iteration tracker
|
|
42
|
+
.claude/docs/adr/ Architectural Decision Records
|
|
43
|
+
```
|
|
51
44
|
|
|
52
|
-
**
|
|
45
|
+
**Pipeline Agents** — research, dev, plan, qa, devops, security — fetched from the PRISM network so they're always up to date.
|
|
53
46
|
|
|
54
|
-
|
|
47
|
+
**Health Check** — daily GitHub Action with auto-remediation (npm audit fix PRs).
|
|
55
48
|
|
|
56
|
-
|
|
57
|
-
# Scaffold a new project
|
|
58
|
-
npx syntropic init my-project
|
|
49
|
+
## Track Record
|
|
59
50
|
|
|
60
|
-
|
|
61
|
-
npx syntropic init my-project --tools claude,cursor
|
|
51
|
+
Built in production on a real product (27 AI agents, 8-lens analyser, CLI, 7 free tools) over 10 weeks:
|
|
62
52
|
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
- **123 features shipped**, each through a structured pipeline
|
|
54
|
+
- **12 test→production promotions** — every feature verified on test page first
|
|
55
|
+
- **3 bug classes prevented** by specific EG rules before reaching users
|
|
56
|
+
- **0 undetected production incidents** after methodology adoption
|
|
65
57
|
|
|
66
|
-
|
|
67
|
-
npx syntropic add cursor
|
|
68
|
-
npx syntropic add windsurf copilot
|
|
58
|
+
## Trust & Privacy
|
|
69
59
|
|
|
70
|
-
|
|
71
|
-
npx syntropic health
|
|
60
|
+
**Everything is local.** Your `.claude/` directory — rules, agents, governance docs — lives in your repo. It's yours.
|
|
72
61
|
|
|
73
|
-
|
|
74
|
-
npx syntropic telemetry status
|
|
75
|
-
npx syntropic telemetry disable
|
|
76
|
-
npx syntropic telemetry enable
|
|
62
|
+
**Telemetry is optional.** PRISM collects anonymous structural metadata (cycle weight, file count, framework — never code, names, or content). Double-hashed before transmission. Default: on. Opt out anytime:
|
|
77
63
|
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
```bash
|
|
65
|
+
syntropic telemetry disable
|
|
80
66
|
```
|
|
81
67
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
Running `syntropic init` in a project that already has a `CLAUDE.md` or `.cursorrules` will **append** the methodology to your existing file — it never overwrites. A `<!-- syntropic -->` marker prevents duplicate rules on re-runs.
|
|
68
|
+
**What's collected:** cycle weight, phases, success/failure, tool, OS, project shape.
|
|
69
|
+
**Never collected:** file contents, names, code, diffs, commits, identity, API keys.
|
|
85
70
|
|
|
86
|
-
|
|
71
|
+
**Clean exit.** Remove everything syntropic added (preserves your governance docs):
|
|
87
72
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
73
|
+
```bash
|
|
74
|
+
syntropic remove
|
|
75
|
+
```
|
|
91
76
|
|
|
92
|
-
|
|
93
|
-
2. **Plan** — break work into sequenced steps, identify risks
|
|
94
|
-
3. **Build** — implement with minimal changes, match existing patterns
|
|
95
|
-
4. **Review** — check security, quality, and correctness
|
|
96
|
-
5. **Verify** — test on a live URL, not just locally
|
|
77
|
+
**Inspect the output.** Run `npm pack` on the package — everything that ships is plain text templates. The methodology is served from a public API endpoint you can curl yourself.
|
|
97
78
|
|
|
98
|
-
|
|
79
|
+
## Commands
|
|
99
80
|
|
|
100
|
-
|
|
81
|
+
```bash
|
|
82
|
+
syntropic audit # Analyse git history (no install needed)
|
|
83
|
+
syntropic init [project-name] # Install methodology + docs
|
|
84
|
+
syntropic add cursor windsurf # Add tools to existing project
|
|
85
|
+
syntropic remove # Clean uninstall (preserves docs)
|
|
86
|
+
syntropic health # Run pre-flight checks
|
|
87
|
+
syntropic report # Submit anonymous cycle report
|
|
88
|
+
syntropic telemetry status # Check telemetry setting
|
|
89
|
+
syntropic analyse # Deep-dive analysis (8 philosophy lenses)
|
|
90
|
+
```
|
|
101
91
|
|
|
102
|
-
|
|
92
|
+
## How It Works
|
|
103
93
|
|
|
104
|
-
|
|
94
|
+
Your instruction file contains a bootstrap rule (EG13) that fetches the full methodology from the PRISM network at cycle start. Rules, agents, and patterns are served fresh — always current, zero maintenance. Your LLM does all compute. Syntropic serves the methodology but runs zero inference.
|
|
105
95
|
|
|
106
|
-
|
|
96
|
+
The governance docs give your AI assistant project memory — it checks priorities before coding and logs issues after. Over time, your docs become a structured record of every decision, bug, and iteration.
|
|
107
97
|
|
|
108
|
-
|
|
98
|
+
## Existing Projects
|
|
109
99
|
|
|
110
|
-
|
|
100
|
+
Running `syntropic init` in a project that already has a `CLAUDE.md` or `.cursorrules` **appends** the methodology — never overwrites. A `<!-- syntropic -->` marker prevents duplicates.
|
|
111
101
|
|
|
112
|
-
##
|
|
102
|
+
## Research
|
|
113
103
|
|
|
114
|
-
|
|
115
|
-
- **Hypothesis-driven** — test before you code
|
|
116
|
-
- **Outcome-aligned** — if the user succeeds, do we succeed?
|
|
117
|
-
- **No workarounds** — no bypasses, no "fix later", no shortcuts without approval
|
|
104
|
+
The methodology is grounded in published research: [zenodo.org/records/17894441](https://zenodo.org/records/17894441)
|
|
118
105
|
|
|
119
106
|
## Links
|
|
120
107
|
|
|
121
108
|
- [Website](https://www.syntropicworks.com)
|
|
122
|
-
- [
|
|
123
|
-
- [Research Paper](https://zenodo.org/records/17894441) — the underlying research behind the methodology
|
|
124
|
-
- [Intelligent Analyser](https://www.syntropicworks.com/analyser) — deep-dive analysis through 8 philosophy lenses
|
|
109
|
+
- [Intelligent Analyser](https://www.syntropicworks.com/analyser) — deep-dive product analysis through 8 philosophy lenses
|
|
125
110
|
|
|
126
111
|
## License
|
|
127
112
|
|
package/bin/syntropic.js
CHANGED
|
@@ -14,8 +14,10 @@ const args = process.argv.slice(2);
|
|
|
14
14
|
const command = args[0];
|
|
15
15
|
|
|
16
16
|
const COMMANDS = {
|
|
17
|
+
audit: () => require('../commands/audit'),
|
|
17
18
|
init: () => require('../commands/init'),
|
|
18
19
|
add: () => require('../commands/add'),
|
|
20
|
+
remove: () => require('../commands/remove'),
|
|
19
21
|
health: () => require('../commands/health'),
|
|
20
22
|
telemetry: () => require('../commands/telemetry'),
|
|
21
23
|
report: () => require('../commands/report'),
|
|
@@ -39,8 +41,10 @@ if (!command || args.includes('--help') || args.includes('-h')) {
|
|
|
39
41
|
syntropic — Philosophy-as-code development pipeline
|
|
40
42
|
|
|
41
43
|
Usage:
|
|
44
|
+
syntropic audit Analyse your git history for process issues (no install needed)
|
|
42
45
|
syntropic init [project-name] Scaffold a new project with the Syntropic pipeline
|
|
43
46
|
syntropic add <tool> [tool...] Add support for another AI tool
|
|
47
|
+
syntropic remove Remove syntropic from this project (preserves your docs)
|
|
44
48
|
syntropic analyse Analyse your product using 8 philosophy lenses
|
|
45
49
|
syntropic login Sign in to Syntropic (required for analyse)
|
|
46
50
|
syntropic logout Sign out
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* syntropic audit [--commits <n>]
|
|
3
|
+
*
|
|
4
|
+
* Analyse your git history for development process issues.
|
|
5
|
+
* Proves value on your own code before installing anything.
|
|
6
|
+
* Pure local analysis — sends nothing, requires nothing.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
const DEFAULTS = { commits: 20 };
|
|
14
|
+
|
|
15
|
+
function parseFlags(args) {
|
|
16
|
+
const flags = {};
|
|
17
|
+
for (let i = 0; i < args.length; i++) {
|
|
18
|
+
if (args[i] === '--help' || args[i] === '-h') { flags.help = true; continue; }
|
|
19
|
+
if (args[i].startsWith('--')) {
|
|
20
|
+
const key = args[i].replace('--', '');
|
|
21
|
+
flags[key] = args[i + 1] && !args[i + 1].startsWith('--') ? args[++i] : true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return flags;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function git(cmd) {
|
|
28
|
+
try {
|
|
29
|
+
return execSync(`git ${cmd}`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
30
|
+
} catch {
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function colour(text, code) {
|
|
36
|
+
if (!process.stdout.isTTY) return text;
|
|
37
|
+
return `\x1b[${code}m${text}\x1b[0m`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const green = (t) => colour(t, '32');
|
|
41
|
+
const yellow = (t) => colour(t, '33');
|
|
42
|
+
const red = (t) => colour(t, '31');
|
|
43
|
+
const dim = (t) => colour(t, '2');
|
|
44
|
+
const bold = (t) => colour(t, '1');
|
|
45
|
+
|
|
46
|
+
function checkLocalhost(n) {
|
|
47
|
+
// Find commits that introduced localhost/127.0.0.1 references
|
|
48
|
+
const log = git(`log --oneline -${n} --format=%H`);
|
|
49
|
+
if (!log) return { issues: 0, detail: '' };
|
|
50
|
+
|
|
51
|
+
const hashes = log.split('\n').filter(Boolean);
|
|
52
|
+
let hits = 0;
|
|
53
|
+
const examples = [];
|
|
54
|
+
|
|
55
|
+
for (const hash of hashes) {
|
|
56
|
+
const diff = git(`diff ${hash}~1 ${hash} -- . ":(exclude)*.lock" ":(exclude)node_modules" 2>/dev/null`);
|
|
57
|
+
if (!diff) continue;
|
|
58
|
+
|
|
59
|
+
const addedLines = diff.split('\n').filter(l => l.startsWith('+') && !l.startsWith('+++'));
|
|
60
|
+
for (const line of addedLines) {
|
|
61
|
+
if (/localhost|127\.0\.0\.1/.test(line) && !/\/\/.*localhost/.test(line.replace(/https?:\/\/localhost/, 'MATCH'))) {
|
|
62
|
+
hits++;
|
|
63
|
+
if (examples.length < 2) {
|
|
64
|
+
const shortHash = hash.slice(0, 7);
|
|
65
|
+
const subject = git(`log --format=%s -1 ${hash}`);
|
|
66
|
+
examples.push(`${shortHash} ${subject}`);
|
|
67
|
+
}
|
|
68
|
+
break; // Count once per commit
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
issues: hits,
|
|
75
|
+
detail: examples.length ? examples.map(e => ` ${dim(e)}`).join('\n') : '',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function checkEnvFiles(n) {
|
|
80
|
+
// Check if .env files are tracked in git
|
|
81
|
+
const tracked = git('ls-files').split('\n');
|
|
82
|
+
const envFiles = tracked.filter(f => {
|
|
83
|
+
const base = path.basename(f);
|
|
84
|
+
// .env.example, .env.sample, .env.template are fine
|
|
85
|
+
return /^\.env($|\.)/.test(base) && !/\.(example|sample|template)$/.test(base);
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
issues: envFiles.length,
|
|
89
|
+
detail: envFiles.length ? envFiles.map(f => ` ${dim(f)}`).join('\n') : '',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function checkCommitConventions(n) {
|
|
94
|
+
const log = git(`log --oneline -${n} --no-merges --format=%s`);
|
|
95
|
+
if (!log) return { conventional: 0, total: 0, pct: 0 };
|
|
96
|
+
|
|
97
|
+
const subjects = log.split('\n').filter(Boolean);
|
|
98
|
+
const conventional = subjects.filter(s => /^(feat|fix|refactor|chore|docs|style|perf|test|ci|build|revert|sync|content|debug|security|spec)(\(.+\))?:/.test(s));
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
conventional: conventional.length,
|
|
102
|
+
total: subjects.length,
|
|
103
|
+
pct: subjects.length > 0 ? Math.round(conventional.length / subjects.length * 100) : 0,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function checkFixRatio(n) {
|
|
108
|
+
const log = git(`log --oneline -${n} --no-merges --format=%s`);
|
|
109
|
+
if (!log) return { feats: 0, fixes: 0, ratio: 0, total: 0 };
|
|
110
|
+
|
|
111
|
+
const subjects = log.split('\n').filter(Boolean);
|
|
112
|
+
const feats = subjects.filter(s => /^feat(\(.+\))?:/.test(s)).length;
|
|
113
|
+
const fixes = subjects.filter(s => /^fix(\(.+\))?:/.test(s)).length;
|
|
114
|
+
const total = subjects.length;
|
|
115
|
+
|
|
116
|
+
return { feats, fixes, ratio: total > 0 ? Math.round(fixes / total * 100) : 0, total };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function checkFilesPerCommit(n) {
|
|
120
|
+
const log = git(`log --oneline -${n} --no-merges --format=%H`);
|
|
121
|
+
if (!log) return { avg: 0, max: 0 };
|
|
122
|
+
|
|
123
|
+
const hashes = log.split('\n').filter(Boolean);
|
|
124
|
+
const counts = hashes.map(h => {
|
|
125
|
+
const files = git(`diff --name-only ${h}~1 ${h} 2>/dev/null`);
|
|
126
|
+
return files ? files.split('\n').filter(Boolean).length : 0;
|
|
127
|
+
}).filter(c => c > 0);
|
|
128
|
+
|
|
129
|
+
if (counts.length === 0) return { avg: 0, max: 0 };
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
avg: Math.round(counts.reduce((a, b) => a + b, 0) / counts.length * 10) / 10,
|
|
133
|
+
max: Math.max(...counts),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function checkGovernanceDocs() {
|
|
138
|
+
const cwd = process.cwd();
|
|
139
|
+
const docs = {
|
|
140
|
+
north_star: fs.existsSync(path.join(cwd, '.claude', 'docs', 'NORTH_STAR.md')) || fs.existsSync(path.join(cwd, '.claude', 'NORTH_STAR.md')),
|
|
141
|
+
backlog: fs.existsSync(path.join(cwd, '.claude', 'docs', 'BACKLOG.md')),
|
|
142
|
+
issues: fs.existsSync(path.join(cwd, '.claude', 'docs', 'ISSUES.md')),
|
|
143
|
+
adr: fs.existsSync(path.join(cwd, '.claude', 'docs', 'adr')),
|
|
144
|
+
};
|
|
145
|
+
const count = Object.values(docs).filter(Boolean).length;
|
|
146
|
+
return { count, total: 4, docs };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function computeScore(results) {
|
|
150
|
+
let score = 5; // Start at 5/10
|
|
151
|
+
|
|
152
|
+
// Localhost issues: -1 per issue, max -2
|
|
153
|
+
score -= Math.min(results.localhost.issues, 2);
|
|
154
|
+
|
|
155
|
+
// Env files tracked: -2 if any
|
|
156
|
+
if (results.envFiles.issues > 0) score -= 2;
|
|
157
|
+
|
|
158
|
+
// Conventional commits: +1 if >70%, +0.5 if >40%
|
|
159
|
+
if (results.conventions.pct >= 70) score += 1;
|
|
160
|
+
else if (results.conventions.pct >= 40) score += 0.5;
|
|
161
|
+
|
|
162
|
+
// Fix ratio: neutral if 30-50%, -0.5 if >60%
|
|
163
|
+
if (results.fixRatio.ratio > 60) score -= 0.5;
|
|
164
|
+
|
|
165
|
+
// Governance docs: +0.5 per doc present
|
|
166
|
+
score += results.governance.count * 0.5;
|
|
167
|
+
|
|
168
|
+
// Avg files per commit: -0.5 if >15 (giant commits)
|
|
169
|
+
if (results.filesPerCommit.avg > 15) score -= 0.5;
|
|
170
|
+
|
|
171
|
+
return Math.max(0, Math.min(10, Math.round(score * 2) / 2));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function run(args) {
|
|
175
|
+
const flags = parseFlags(args);
|
|
176
|
+
|
|
177
|
+
if (flags.help) {
|
|
178
|
+
console.log(`
|
|
179
|
+
syntropic audit — Analyse your git history for process issues
|
|
180
|
+
|
|
181
|
+
Usage:
|
|
182
|
+
syntropic audit [options]
|
|
183
|
+
npx syntropic audit (no install needed)
|
|
184
|
+
|
|
185
|
+
Options:
|
|
186
|
+
--commits <n> Number of recent commits to analyse (default: 20)
|
|
187
|
+
|
|
188
|
+
What it checks:
|
|
189
|
+
- Localhost/127.0.0.1 references committed to repo
|
|
190
|
+
- .env files tracked in git
|
|
191
|
+
- Commit message conventions
|
|
192
|
+
- Fix:feature ratio (iteration indicator)
|
|
193
|
+
- Average files per commit (scope discipline)
|
|
194
|
+
- Governance docs (North Star, Backlog, Issues, ADRs)
|
|
195
|
+
|
|
196
|
+
This is a local-only analysis. Nothing is sent anywhere.
|
|
197
|
+
`);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check we're in a git repo
|
|
202
|
+
const gitRoot = git('rev-parse --show-toplevel');
|
|
203
|
+
if (!gitRoot) {
|
|
204
|
+
console.error('\n Not a git repository. Run this from a project with git history.\n');
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const n = parseInt(flags.commits, 10) || DEFAULTS.commits;
|
|
209
|
+
const commitCount = git('rev-list --count HEAD');
|
|
210
|
+
const actualN = Math.min(n, parseInt(commitCount, 10) || n);
|
|
211
|
+
|
|
212
|
+
console.log(`\n ${bold('syntropic audit')} — last ${actualN} commits\n`);
|
|
213
|
+
|
|
214
|
+
// Run all checks
|
|
215
|
+
const results = {
|
|
216
|
+
localhost: checkLocalhost(actualN),
|
|
217
|
+
envFiles: checkEnvFiles(actualN),
|
|
218
|
+
conventions: checkCommitConventions(actualN),
|
|
219
|
+
fixRatio: checkFixRatio(actualN),
|
|
220
|
+
filesPerCommit: checkFilesPerCommit(actualN),
|
|
221
|
+
governance: checkGovernanceDocs(),
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const score = computeScore(results);
|
|
225
|
+
|
|
226
|
+
// Display results
|
|
227
|
+
const PAD = 24;
|
|
228
|
+
|
|
229
|
+
// EG1: Localhost
|
|
230
|
+
if (results.localhost.issues > 0) {
|
|
231
|
+
console.log(` ${yellow('⚠')} ${'EG1 Pre-flight'.padEnd(PAD)} ${yellow(`${results.localhost.issues} commit${results.localhost.issues > 1 ? 's' : ''} contain localhost references`)}`);
|
|
232
|
+
if (results.localhost.detail) console.log(results.localhost.detail);
|
|
233
|
+
} else {
|
|
234
|
+
console.log(` ${green('✓')} ${'EG1 Pre-flight'.padEnd(PAD)} ${green('No localhost references found')}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// EG10: Env files
|
|
238
|
+
if (results.envFiles.issues > 0) {
|
|
239
|
+
console.log(` ${red('✗')} ${'EG10 Env hygiene'.padEnd(PAD)} ${red(`${results.envFiles.issues} .env file${results.envFiles.issues > 1 ? 's' : ''} tracked in git`)}`);
|
|
240
|
+
if (results.envFiles.detail) console.log(results.envFiles.detail);
|
|
241
|
+
} else {
|
|
242
|
+
console.log(` ${green('✓')} ${'EG10 Env hygiene'.padEnd(PAD)} ${green('No .env files tracked')}`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Commit conventions
|
|
246
|
+
const convPct = results.conventions.pct;
|
|
247
|
+
const convIcon = convPct >= 70 ? green('✓') : convPct >= 40 ? yellow('⚠') : red('✗');
|
|
248
|
+
const convColour = convPct >= 70 ? green : convPct >= 40 ? yellow : red;
|
|
249
|
+
console.log(` ${convIcon} ${'Commit discipline'.padEnd(PAD)} ${convColour(`${convPct}% conventional commits`)} ${dim(`(${results.conventions.conventional}/${results.conventions.total})`)}`);
|
|
250
|
+
|
|
251
|
+
// Fix ratio
|
|
252
|
+
const fixPct = results.fixRatio.ratio;
|
|
253
|
+
const fixIcon = fixPct <= 50 ? green('✓') : yellow('⚠');
|
|
254
|
+
const fixColour = fixPct <= 50 ? green : yellow;
|
|
255
|
+
console.log(` ${fixIcon} ${'Fix ratio'.padEnd(PAD)} ${fixColour(`${fixPct}% of commits are fixes`)} ${dim(`(${results.fixRatio.fixes} fixes, ${results.fixRatio.feats} features)`)}`);
|
|
256
|
+
|
|
257
|
+
// Files per commit
|
|
258
|
+
const avgFiles = results.filesPerCommit.avg;
|
|
259
|
+
const filesIcon = avgFiles <= 10 ? green('✓') : avgFiles <= 15 ? yellow('⚠') : red('✗');
|
|
260
|
+
const filesColour = avgFiles <= 10 ? green : avgFiles <= 15 ? yellow : red;
|
|
261
|
+
console.log(` ${filesIcon} ${'Scope discipline'.padEnd(PAD)} ${filesColour(`${avgFiles} avg files/commit`)} ${dim(`(max: ${results.filesPerCommit.max})`)}`);
|
|
262
|
+
|
|
263
|
+
// Governance docs
|
|
264
|
+
const govCount = results.governance.count;
|
|
265
|
+
const govIcon = govCount >= 3 ? green('✓') : govCount >= 1 ? yellow('⚠') : dim('○');
|
|
266
|
+
const govColour = govCount >= 3 ? green : govCount >= 1 ? yellow : dim;
|
|
267
|
+
const govNames = [];
|
|
268
|
+
if (results.governance.docs.north_star) govNames.push('North Star');
|
|
269
|
+
if (results.governance.docs.backlog) govNames.push('Backlog');
|
|
270
|
+
if (results.governance.docs.issues) govNames.push('Issues');
|
|
271
|
+
if (results.governance.docs.adr) govNames.push('ADRs');
|
|
272
|
+
console.log(` ${govIcon} ${'Governance docs'.padEnd(PAD)} ${govColour(`${govCount}/4 present`)} ${dim(govNames.length ? `(${govNames.join(', ')})` : '(none)')}`);
|
|
273
|
+
|
|
274
|
+
// Score
|
|
275
|
+
console.log('');
|
|
276
|
+
const scoreColour = score >= 7 ? green : score >= 4 ? yellow : red;
|
|
277
|
+
console.log(` ${bold('Score:')} ${scoreColour(`${score}/10`)}`);
|
|
278
|
+
|
|
279
|
+
// Recommendation
|
|
280
|
+
console.log('');
|
|
281
|
+
if (score >= 8) {
|
|
282
|
+
console.log(` ${green('Strong development discipline.')} Syntropic can keep it that way.`);
|
|
283
|
+
} else if (score >= 5) {
|
|
284
|
+
console.log(` ${yellow('Room for improvement.')} Common issues that a structured pipeline prevents.`);
|
|
285
|
+
} else {
|
|
286
|
+
console.log(` ${red('Significant process gaps.')} A development methodology would catch these early.`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
console.log(`\n ${dim('Run `syntropic init` to install the methodology and prevent these automatically.')}`);
|
|
290
|
+
console.log(` ${dim('Learn more: https://www.syntropicworks.com')}\n`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = run;
|