syntropic 0.8.1 → 0.9.1
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 +76 -76
- package/bin/syntropic.js +4 -0
- package/commands/audit.js +293 -0
- package/commands/init.js +65 -13
- package/commands/remove.js +220 -0
- package/commands/report.js +52 -1
- package/package.json +5 -2
- package/templates/CLAUDE.md +27 -1
- package/templates/agents-md.template +17 -1
- package/templates/claude-append.template +11 -1
- package/templates/copilot-instructions.template +15 -1
- package/templates/cursorrules.template +17 -1
- 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 +11 -1
- package/templates/windsurfrules.template +17 -1
package/README.md
CHANGED
|
@@ -1,127 +1,127 @@
|
|
|
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
|
+
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.
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|------|---------------|
|
|
24
|
-
| Claude Code | `CLAUDE.md` |
|
|
25
|
-
| Cursor | `.cursorrules` |
|
|
26
|
-
| Windsurf | `.windsurfrules` |
|
|
27
|
-
| GitHub Copilot | `.github/copilot-instructions.md` |
|
|
28
|
-
| OpenAI Codex | `AGENTS.md` |
|
|
23
|
+
## What You Get
|
|
29
24
|
|
|
30
|
-
|
|
25
|
+
**Evergreen Rules** — battle-tested development rules fetched fresh at every cycle:
|
|
31
26
|
|
|
32
|
-
|
|
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 |
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
**Governance Docs** — project management templates your AI assistant reads before every cycle:
|
|
35
37
|
|
|
36
|
-
|
|
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
|
+
```
|
|
37
44
|
|
|
38
|
-
**
|
|
45
|
+
**Pipeline Agents** — research, dev, plan, qa, devops, security — fetched from the PRISM network so they're always up to date.
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
- **EG2: Ship and Iterate** — bias to action, only pause for destructive/expensive/irreversible
|
|
42
|
-
- **EG3: Hypothesis-Driven Debugging** — state hypothesis, test without code changes, only code if confirmed
|
|
43
|
-
- **EG7: Implementation Pipeline** — scale process to task size (Full / Lightweight / Minimum cycle)
|
|
44
|
-
- **EG8: Live Site Testing** — test page first, promote to production with approval
|
|
45
|
-
- **EG9: Session Continuity** — re-read rules on reset, verify state before proceeding
|
|
46
|
-
- **EG13: PRISM Sync** — fetch methodology at cycle start, report at cycle end
|
|
47
|
+
**Health Check** — daily GitHub Action with auto-remediation (npm audit fix PRs).
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
## Track Record
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
Built in production on a real product (27 AI agents, 8-lens analyser, CLI, 7 free tools) over 10 weeks:
|
|
51
52
|
|
|
52
|
-
|
|
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
|
|
53
57
|
|
|
54
|
-
##
|
|
58
|
+
## Trust & Privacy
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
# Scaffold a new project
|
|
58
|
-
npx syntropic init my-project
|
|
60
|
+
**Everything is local.** Your `.claude/` directory — rules, agents, governance docs — lives in your repo. It's yours.
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
npx syntropic init my-project --tools claude,cursor
|
|
62
|
+
**Telemetry is metadata only.** PRISM learns from anonymous structural metadata about development cycles — never from your code, file contents, or project details. This metadata is what drives methodology improvements: which cycle weights work best for which project shapes, where pipelines break down, what patterns lead to first-pass success.
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
**What's collected:** cycle weight, phases run, success/failure, tool used, OS, framework detected, file count bucket (1-5 / 6-20 / 21+), governance doc presence.
|
|
65
|
+
**Never collected:** file contents, file names, code, diffs, commit messages, project names, identity, API keys.
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
npx syntropic add cursor
|
|
68
|
-
npx syntropic add windsurf copilot
|
|
67
|
+
**Why it matters.** The PRISM network grows exponentially with contributors. Every anonymous report adds signal — which cycle weights work for which project shapes, where pipelines break down, what patterns lead to first-pass success. More contributors = better data = smarter rules for everyone.
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
npx syntropic health
|
|
69
|
+
**Contributors get more.** When telemetry is enabled, your PRISM sync fetches live network benchmarks — real success rates, iteration counts, and pattern intelligence computed from aggregate data across all contributors. When telemetry is disabled, you still get the full EG rules and agents, but benchmarks are static baselines only. No account needed — your identity is a double-hashed device fingerprint ([pseudonymisation details](https://zenodo.org/records/17894441)).
|
|
72
70
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
| | Community (telemetry off) | Contributor (telemetry on) |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| EG rules | Full | Full |
|
|
74
|
+
| Pipeline agents | Full | Full |
|
|
75
|
+
| Governance docs | Full | Full |
|
|
76
|
+
| Benchmarks | Static baselines | **Live network data** |
|
|
77
|
+
| Pattern intelligence | Frozen snapshot | **Updated from aggregate reports** |
|
|
78
|
+
| Network benefit | None | **Your reports improve rules for everyone** |
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
npx syntropic report --weight lightweight --phases research,plan,dev,qa,test --success true --iterations 1
|
|
80
|
-
```
|
|
80
|
+
Default: on. Opt out anytime:
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
## The Methodology
|
|
82
|
+
```bash
|
|
83
|
+
syntropic telemetry disable
|
|
84
|
+
```
|
|
87
85
|
|
|
88
|
-
|
|
86
|
+
**Clean exit.** Remove everything syntropic added (preserves your governance docs):
|
|
89
87
|
|
|
90
|
-
|
|
88
|
+
```bash
|
|
89
|
+
syntropic remove
|
|
90
|
+
```
|
|
91
91
|
|
|
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
|
|
92
|
+
**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](https://www.syntropicworks.com/api/v1/prism/config) you can curl yourself.
|
|
97
93
|
|
|
98
|
-
|
|
94
|
+
## Commands
|
|
99
95
|
|
|
100
|
-
|
|
96
|
+
```bash
|
|
97
|
+
syntropic audit # Analyse git history (no install needed)
|
|
98
|
+
syntropic init [project-name] # Install methodology + docs
|
|
99
|
+
syntropic add cursor windsurf # Add tools to existing project
|
|
100
|
+
syntropic remove # Clean uninstall (preserves docs)
|
|
101
|
+
syntropic health # Run pre-flight checks
|
|
102
|
+
syntropic report # Submit anonymous cycle report
|
|
103
|
+
syntropic telemetry status # Check telemetry setting
|
|
104
|
+
syntropic analyse # Deep-dive analysis (8 philosophy lenses)
|
|
105
|
+
```
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
## How It Works
|
|
103
108
|
|
|
104
|
-
|
|
109
|
+
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
110
|
|
|
106
|
-
|
|
111
|
+
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
112
|
|
|
108
|
-
|
|
113
|
+
## Existing Projects
|
|
109
114
|
|
|
110
|
-
|
|
115
|
+
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
116
|
|
|
112
|
-
##
|
|
117
|
+
## Research
|
|
113
118
|
|
|
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
|
|
119
|
+
The methodology is grounded in published research: [zenodo.org/records/17894441](https://zenodo.org/records/17894441)
|
|
118
120
|
|
|
119
121
|
## Links
|
|
120
122
|
|
|
121
123
|
- [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
|
|
124
|
+
- [Intelligent Analyser](https://www.syntropicworks.com/analyser) — deep-dive product analysis through 8 philosophy lenses
|
|
125
125
|
|
|
126
126
|
## License
|
|
127
127
|
|
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;
|
package/commands/init.js
CHANGED
|
@@ -186,6 +186,51 @@ function detectTools(targetDir) {
|
|
|
186
186
|
return detected;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
function scaffoldDocs(targetDir, projectName) {
|
|
190
|
+
const docsDir = path.join(targetDir, '.claude', 'docs');
|
|
191
|
+
const adrDir = path.join(docsDir, 'adr');
|
|
192
|
+
const docsTemplateDir = path.join(TEMPLATES_DIR, 'docs');
|
|
193
|
+
|
|
194
|
+
// Skip if docs dir already exists with content
|
|
195
|
+
if (fs.existsSync(docsDir)) {
|
|
196
|
+
const existing = fs.readdirSync(docsDir).filter(f => !f.startsWith('.'));
|
|
197
|
+
if (existing.length > 0) {
|
|
198
|
+
console.log(` skip .claude/docs/ (already exists with ${existing.length} items)`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!fs.existsSync(docsTemplateDir)) return;
|
|
204
|
+
|
|
205
|
+
fs.mkdirSync(adrDir, { recursive: true });
|
|
206
|
+
|
|
207
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
208
|
+
const replacements = {
|
|
209
|
+
'\\{\\{PROJECT_NAME\\}\\}': projectName,
|
|
210
|
+
'\\{\\{CREATED_DATE\\}\\}': today,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Copy doc templates, renaming .template to .md
|
|
214
|
+
const docFiles = [
|
|
215
|
+
{ src: 'NORTH_STAR.template', dest: path.join(docsDir, 'NORTH_STAR.md') },
|
|
216
|
+
{ src: 'BACKLOG.template', dest: path.join(docsDir, 'BACKLOG.md') },
|
|
217
|
+
{ src: 'ISSUES.template', dest: path.join(docsDir, 'ISSUES.md') },
|
|
218
|
+
{ src: path.join('adr', '000-TEMPLATE.template'), dest: path.join(adrDir, '000-TEMPLATE.md') },
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
for (const { src, dest } of docFiles) {
|
|
222
|
+
const srcPath = path.join(docsTemplateDir, src);
|
|
223
|
+
if (!fs.existsSync(srcPath)) continue;
|
|
224
|
+
|
|
225
|
+
let content = fs.readFileSync(srcPath, 'utf8');
|
|
226
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
227
|
+
content = content.replace(new RegExp(key, 'g'), () => value);
|
|
228
|
+
}
|
|
229
|
+
fs.writeFileSync(dest, content, 'utf8');
|
|
230
|
+
console.log(` create ${path.relative(process.cwd(), dest)}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
189
234
|
async function run(args) {
|
|
190
235
|
console.log('\n syntropic init — Philosophy-as-code development pipeline\n');
|
|
191
236
|
|
|
@@ -261,6 +306,9 @@ async function run(args) {
|
|
|
261
306
|
actions[tool] = writeOrAppend(fullTpl, appendTpl, destPath, replacements);
|
|
262
307
|
}
|
|
263
308
|
|
|
309
|
+
// Scaffold governance docs (.claude/docs/)
|
|
310
|
+
scaffoldDocs(targetDir, projectName);
|
|
311
|
+
|
|
264
312
|
// Make health check executable
|
|
265
313
|
const healthScript = path.join(targetDir, 'scripts', 'health-check.js');
|
|
266
314
|
if (fs.existsSync(healthScript)) {
|
|
@@ -288,6 +336,7 @@ async function run(args) {
|
|
|
288
336
|
|
|
289
337
|
What was created:
|
|
290
338
|
${toolFiles}
|
|
339
|
+
.claude/docs/ Project governance docs (North Star, Backlog, Issues, ADRs)
|
|
291
340
|
.github/workflows/ Daily health check with auto-remediation
|
|
292
341
|
scripts/health-check.js Local health check script${hookLine}
|
|
293
342
|
|
|
@@ -299,23 +348,26 @@ ${toolFiles}
|
|
|
299
348
|
agents, and learning data are always up to date — zero local files to
|
|
300
349
|
maintain. All compute runs on your machine.
|
|
301
350
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
351
|
+
Your governance docs:
|
|
352
|
+
.claude/docs/NORTH_STAR.md Define your vision, goals, and principles
|
|
353
|
+
.claude/docs/BACKLOG.md Prioritise and track your work
|
|
354
|
+
.claude/docs/ISSUES.md Log bugs and track iterations
|
|
355
|
+
.claude/docs/adr/ Record architectural decisions
|
|
305
356
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
for everyone. All identifiers are double-hashed before transmission — no
|
|
309
|
-
device ID, code, file names, or personal data is ever stored server-side.
|
|
310
|
-
Run \`syntropic telemetry disable\` to opt out at any time.
|
|
357
|
+
These are YOUR docs — they stay even if you remove syntropic.
|
|
358
|
+
Your AI reads them before every cycle and updates them after.
|
|
311
359
|
|
|
312
|
-
|
|
360
|
+
Next steps:
|
|
361
|
+
1. Fill in your North Star — vision, mission, and goals
|
|
362
|
+
2. Review your instruction file and add project-specific context
|
|
363
|
+
3. Start building! Your AI follows the EG7 pipeline automatically
|
|
313
364
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
365
|
+
Trust & privacy:
|
|
366
|
+
Everything in .claude/ is local and yours. PRISM telemetry is
|
|
367
|
+
pseudonymised (double-hashed) and opt-out: \`syntropic telemetry disable\`.
|
|
368
|
+
Remove everything: \`syntropic remove\` (preserves your docs).
|
|
317
369
|
|
|
318
|
-
|
|
370
|
+
Run \`syntropic audit\` to see what this methodology catches on your existing code.
|
|
319
371
|
|
|
320
372
|
Learn more: https://www.syntropicworks.com
|
|
321
373
|
`);
|