syntropic 0.1.0 → 0.3.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/bin/syntropic.js +13 -2
- package/commands/add.js +195 -0
- package/commands/health.js +22 -9
- package/commands/init.js +148 -27
- package/package.json +5 -2
- package/templates/CLAUDE.md +1 -0
- package/templates/claude-append.template +34 -0
- package/templates/copilot-instructions.template +68 -0
- package/templates/cursorrules.template +85 -0
- package/templates/tool-append.template +32 -0
- package/templates/windsurfrules.template +85 -0
package/bin/syntropic.js
CHANGED
|
@@ -15,6 +15,7 @@ const command = args[0];
|
|
|
15
15
|
|
|
16
16
|
const COMMANDS = {
|
|
17
17
|
init: () => require('../commands/init'),
|
|
18
|
+
add: () => require('../commands/add'),
|
|
18
19
|
health: () => require('../commands/health'),
|
|
19
20
|
};
|
|
20
21
|
|
|
@@ -32,16 +33,26 @@ if (!command || args.includes('--help') || args.includes('-h')) {
|
|
|
32
33
|
|
|
33
34
|
Usage:
|
|
34
35
|
syntropic init [project-name] Scaffold a new project with the Syntropic pipeline
|
|
36
|
+
syntropic add <tool> [tool...] Add support for another AI tool (cursor, windsurf, copilot, claude)
|
|
35
37
|
syntropic health Run a local health check
|
|
36
38
|
syntropic --version Show version
|
|
37
39
|
syntropic --help Show this help
|
|
38
40
|
|
|
39
41
|
What you get:
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
+
- Instruction files for Claude Code, Cursor, Windsurf, and/or GitHub Copilot
|
|
43
|
+
- Evergreen Rules (EG1-EG9) — a disciplined dev pipeline
|
|
44
|
+
- Generic agents: dev, qa, research, plan, devops, security (Claude Code)
|
|
42
45
|
- Daily health check workflow with auto-remediation
|
|
43
46
|
- PRISM efficiency tracking methodology
|
|
44
47
|
|
|
48
|
+
Flags (init):
|
|
49
|
+
--tools claude,cursor,windsurf,copilot Select AI tools (default: all)
|
|
50
|
+
--name "My Project" Project name
|
|
51
|
+
--domain example.com Production domain
|
|
52
|
+
--test-url /test Test page path
|
|
53
|
+
--prod-url / Production page path
|
|
54
|
+
--yes Skip interactive prompts
|
|
55
|
+
|
|
45
56
|
Learn more: https://www.syntropicworks.com
|
|
46
57
|
`);
|
|
47
58
|
process.exit(0);
|
package/commands/add.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* syntropic add <tool> [tool...]
|
|
3
|
+
*
|
|
4
|
+
* Adds Syntropic pipeline support for additional AI coding tools
|
|
5
|
+
* to an existing project. Creates new instruction files or appends
|
|
6
|
+
* rules to existing ones.
|
|
7
|
+
*
|
|
8
|
+
* Examples:
|
|
9
|
+
* syntropic add cursor
|
|
10
|
+
* syntropic add cursor windsurf copilot
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const readline = require('readline');
|
|
16
|
+
|
|
17
|
+
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
|
|
18
|
+
|
|
19
|
+
const TOOLS = {
|
|
20
|
+
claude: { label: 'Claude Code', file: 'CLAUDE.md', hasAgents: true },
|
|
21
|
+
cursor: { label: 'Cursor', file: '.cursorrules', hasAgents: false },
|
|
22
|
+
windsurf: { label: 'Windsurf', file: '.windsurfrules', hasAgents: false },
|
|
23
|
+
copilot: { label: 'GitHub Copilot', file: '.github/copilot-instructions.md', hasAgents: false },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const TEMPLATE_MAP = {
|
|
27
|
+
claude: ['CLAUDE.md', 'claude-append.template'],
|
|
28
|
+
cursor: ['cursorrules.template', 'tool-append.template'],
|
|
29
|
+
windsurf: ['windsurfrules.template', 'tool-append.template'],
|
|
30
|
+
copilot: ['copilot-instructions.template', 'tool-append.template'],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function ask(question) {
|
|
34
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
rl.question(question, (answer) => {
|
|
37
|
+
rl.close();
|
|
38
|
+
resolve(answer.trim());
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function hasSyntropicMarker(filePath) {
|
|
44
|
+
if (!fs.existsSync(filePath)) return false;
|
|
45
|
+
return fs.readFileSync(filePath, 'utf8').includes('<!-- syntropic -->');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function renderTemplate(templateFile, replacements) {
|
|
49
|
+
const templatePath = path.join(TEMPLATES_DIR, templateFile);
|
|
50
|
+
if (!fs.existsSync(templatePath)) return null;
|
|
51
|
+
let content = fs.readFileSync(templatePath, 'utf8');
|
|
52
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
53
|
+
content = content.replace(new RegExp(key, 'g'), value);
|
|
54
|
+
}
|
|
55
|
+
return content;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function writeOrAppend(fullTemplate, appendTemplate, destPath, replacements) {
|
|
59
|
+
const relPath = path.relative(process.cwd(), destPath);
|
|
60
|
+
const destDir = path.dirname(destPath);
|
|
61
|
+
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
62
|
+
|
|
63
|
+
if (!fs.existsSync(destPath)) {
|
|
64
|
+
const content = renderTemplate(fullTemplate, replacements);
|
|
65
|
+
if (content) fs.writeFileSync(destPath, content, 'utf8');
|
|
66
|
+
console.log(` create ${relPath}`);
|
|
67
|
+
return 'created';
|
|
68
|
+
} else if (hasSyntropicMarker(destPath)) {
|
|
69
|
+
console.log(` skip ${relPath} (Syntropic rules already present)`);
|
|
70
|
+
return 'skipped';
|
|
71
|
+
} else {
|
|
72
|
+
const appendContent = renderTemplate(appendTemplate, replacements);
|
|
73
|
+
if (appendContent) fs.appendFileSync(destPath, appendContent, 'utf8');
|
|
74
|
+
console.log(` append ${relPath} (added Syntropic pipeline rules)`);
|
|
75
|
+
return 'appended';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function detectExistingConfig(targetDir) {
|
|
80
|
+
// Try to read project config from existing Syntropic instruction files
|
|
81
|
+
for (const tool of Object.values(TOOLS)) {
|
|
82
|
+
const filePath = path.join(targetDir, tool.file);
|
|
83
|
+
if (fs.existsSync(filePath)) {
|
|
84
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
85
|
+
const config = {};
|
|
86
|
+
|
|
87
|
+
// Extract test URL
|
|
88
|
+
const testMatch = content.match(/Test page:\s*(\S+)/);
|
|
89
|
+
if (testMatch) config.testUrl = testMatch[1];
|
|
90
|
+
|
|
91
|
+
// Extract prod URL
|
|
92
|
+
const prodMatch = content.match(/Production:\s*(\S+)/);
|
|
93
|
+
if (prodMatch) config.prodUrl = prodMatch[1];
|
|
94
|
+
|
|
95
|
+
// Extract domain
|
|
96
|
+
const domainMatch = content.match(/Production domain:\*?\*?\s*(\S+)/);
|
|
97
|
+
if (domainMatch) config.prodDomain = domainMatch[1];
|
|
98
|
+
|
|
99
|
+
// Extract project name
|
|
100
|
+
const nameMatch = content.match(/## Project:\s*(.+)/);
|
|
101
|
+
if (nameMatch) config.projectName = nameMatch[1].trim();
|
|
102
|
+
|
|
103
|
+
if (Object.keys(config).length > 0) return config;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function run(args) {
|
|
110
|
+
const validTools = Object.keys(TOOLS);
|
|
111
|
+
const requestedTools = args.map(a => a.toLowerCase()).filter(a => validTools.includes(a));
|
|
112
|
+
|
|
113
|
+
if (requestedTools.length === 0) {
|
|
114
|
+
console.log(`
|
|
115
|
+
syntropic add — Add AI tool support to an existing project
|
|
116
|
+
|
|
117
|
+
Usage:
|
|
118
|
+
syntropic add cursor Add Cursor support
|
|
119
|
+
syntropic add windsurf copilot Add multiple tools
|
|
120
|
+
syntropic add claude Add Claude Code support
|
|
121
|
+
|
|
122
|
+
Available tools: ${validTools.join(', ')}
|
|
123
|
+
`);
|
|
124
|
+
process.exit(0);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const targetDir = process.cwd();
|
|
128
|
+
console.log(`\n syntropic add — Adding ${requestedTools.map(t => TOOLS[t].label).join(', ')}\n`);
|
|
129
|
+
|
|
130
|
+
// Try to detect project config from existing instruction files
|
|
131
|
+
const existing = detectExistingConfig(targetDir);
|
|
132
|
+
const isInteractive = process.stdin.isTTY !== false;
|
|
133
|
+
|
|
134
|
+
const dirName = path.basename(targetDir);
|
|
135
|
+
const projectName = existing?.projectName || (isInteractive ? await ask(` Project name (${dirName}): `) : '') || dirName;
|
|
136
|
+
const testUrl = existing?.testUrl || (isInteractive ? await ask(' Test page URL path (e.g. /test): ') : '') || '/test';
|
|
137
|
+
const prodUrl = existing?.prodUrl || (isInteractive ? await ask(' Production URL path (e.g. /): ') : '') || '/';
|
|
138
|
+
const prodDomain = existing?.prodDomain || (isInteractive ? await ask(' Production domain (e.g. example.com): ') : '') || 'example.com';
|
|
139
|
+
|
|
140
|
+
if (existing) {
|
|
141
|
+
console.log(` Detected config: ${projectName} (${prodDomain})\n`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const replacements = {
|
|
145
|
+
'\\{\\{PROJECT_NAME\\}\\}': projectName,
|
|
146
|
+
'\\{\\{TEST_URL\\}\\}': testUrl,
|
|
147
|
+
'\\{\\{PROD_URL\\}\\}': prodUrl,
|
|
148
|
+
'\\{\\{PROD_DOMAIN\\}\\}': prodDomain,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
for (const tool of requestedTools) {
|
|
152
|
+
const [fullTpl, appendTpl] = TEMPLATE_MAP[tool];
|
|
153
|
+
const destPath = path.join(targetDir, TOOLS[tool].file);
|
|
154
|
+
writeOrAppend(fullTpl, appendTpl, destPath, replacements);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// If adding Claude, also copy agents
|
|
158
|
+
if (requestedTools.includes('claude')) {
|
|
159
|
+
const claudeDir = path.join(targetDir, '.claude');
|
|
160
|
+
const templateClaudeDir = path.join(TEMPLATES_DIR, '.claude');
|
|
161
|
+
if (fs.existsSync(templateClaudeDir)) {
|
|
162
|
+
const { copyDir } = require('./init');
|
|
163
|
+
if (typeof copyDir === 'function') {
|
|
164
|
+
copyDir(templateClaudeDir, claudeDir);
|
|
165
|
+
} else {
|
|
166
|
+
// Inline copy for agents
|
|
167
|
+
const commandsDir = path.join(templateClaudeDir, 'commands');
|
|
168
|
+
const destCommandsDir = path.join(claudeDir, 'commands');
|
|
169
|
+
if (fs.existsSync(commandsDir)) {
|
|
170
|
+
if (!fs.existsSync(destCommandsDir)) fs.mkdirSync(destCommandsDir, { recursive: true });
|
|
171
|
+
for (const file of fs.readdirSync(commandsDir)) {
|
|
172
|
+
const dest = path.join(destCommandsDir, file);
|
|
173
|
+
if (!fs.existsSync(dest)) {
|
|
174
|
+
fs.copyFileSync(path.join(commandsDir, file), dest);
|
|
175
|
+
console.log(` create ${path.relative(process.cwd(), dest)}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Copy EVERGREEN_RULES.md
|
|
180
|
+
const egSrc = path.join(templateClaudeDir, 'EVERGREEN_RULES.md');
|
|
181
|
+
const egDest = path.join(claudeDir, 'EVERGREEN_RULES.md');
|
|
182
|
+
if (fs.existsSync(egSrc) && !fs.existsSync(egDest)) {
|
|
183
|
+
fs.copyFileSync(egSrc, egDest);
|
|
184
|
+
console.log(` create ${path.relative(process.cwd(), egDest)}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.log(`
|
|
191
|
+
Done! Added: ${requestedTools.map(t => TOOLS[t].label).join(', ')}
|
|
192
|
+
`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
module.exports = run;
|
package/commands/health.js
CHANGED
|
@@ -15,22 +15,35 @@ function run() {
|
|
|
15
15
|
const checks = [];
|
|
16
16
|
let hasErrors = false;
|
|
17
17
|
|
|
18
|
-
// 1. Check
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
// 1. Check instruction files
|
|
19
|
+
const instructionFiles = [
|
|
20
|
+
{ name: 'Claude Code', path: 'CLAUDE.md' },
|
|
21
|
+
{ name: 'Cursor', path: '.cursorrules' },
|
|
22
|
+
{ name: 'Windsurf', path: '.windsurfrules' },
|
|
23
|
+
{ name: 'GitHub Copilot', path: path.join('.github', 'copilot-instructions.md') },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const foundTools = [];
|
|
27
|
+
for (const tool of instructionFiles) {
|
|
28
|
+
if (fs.existsSync(path.join(process.cwd(), tool.path))) {
|
|
29
|
+
foundTools.push(tool.name);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (foundTools.length > 0) {
|
|
34
|
+
checks.push({ name: 'Instructions', status: 'pass', detail: `Found: ${foundTools.join(', ')}` });
|
|
22
35
|
} else {
|
|
23
|
-
checks.push({ name: '
|
|
36
|
+
checks.push({ name: 'Instructions', status: 'fail', detail: 'No instruction files — run "syntropic init"' });
|
|
24
37
|
hasErrors = true;
|
|
25
38
|
}
|
|
26
39
|
|
|
27
|
-
// 2. Check .claude directory
|
|
40
|
+
// 2. Check .claude directory (Claude Code agents)
|
|
28
41
|
const claudeDir = path.join(process.cwd(), '.claude');
|
|
29
|
-
if (fs.existsSync(claudeDir)) {
|
|
42
|
+
if (fs.existsSync(claudeDir) && fs.existsSync(path.join(claudeDir, 'commands'))) {
|
|
30
43
|
const agents = fs.readdirSync(path.join(claudeDir, 'commands')).filter(f => f.endsWith('.md') && f !== 'README.md');
|
|
31
44
|
checks.push({ name: 'Agents', status: 'pass', detail: `${agents.length} agents found` });
|
|
32
|
-
} else {
|
|
33
|
-
checks.push({ name: 'Agents', status: 'warn', detail: 'No .claude directory' });
|
|
45
|
+
} else if (foundTools.includes('Claude Code')) {
|
|
46
|
+
checks.push({ name: 'Agents', status: 'warn', detail: 'No .claude/commands directory' });
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
// 3. npm build check
|
package/commands/init.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* syntropic init [project-name]
|
|
3
3
|
*
|
|
4
4
|
* Scaffolds a project with the Syntropic development pipeline:
|
|
5
|
-
* -
|
|
6
|
-
* - .claude/ directory with generic agents
|
|
5
|
+
* - Instruction files for Claude Code, Cursor, Windsurf, and/or GitHub Copilot
|
|
6
|
+
* - .claude/ directory with generic agents (Claude Code)
|
|
7
7
|
* - .github/workflows/ with health check + auto-remediation
|
|
8
8
|
* - scripts/health-check.js
|
|
9
9
|
*/
|
|
@@ -14,6 +14,13 @@ const readline = require('readline');
|
|
|
14
14
|
|
|
15
15
|
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
|
|
16
16
|
|
|
17
|
+
const TOOLS = {
|
|
18
|
+
claude: { label: 'Claude Code', file: 'CLAUDE.md', hasAgents: true },
|
|
19
|
+
cursor: { label: 'Cursor', file: '.cursorrules', hasAgents: false },
|
|
20
|
+
windsurf: { label: 'Windsurf', file: '.windsurfrules', hasAgents: false },
|
|
21
|
+
copilot: { label: 'GitHub Copilot', file: '.github/copilot-instructions.md', hasAgents: false },
|
|
22
|
+
};
|
|
23
|
+
|
|
17
24
|
function ask(question) {
|
|
18
25
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
19
26
|
return new Promise((resolve) => {
|
|
@@ -24,15 +31,36 @@ function ask(question) {
|
|
|
24
31
|
});
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
function
|
|
34
|
+
async function askMultiSelect(question, options) {
|
|
35
|
+
console.log(question);
|
|
36
|
+
options.forEach((opt, i) => {
|
|
37
|
+
console.log(` ${i + 1}. ${opt.label}`);
|
|
38
|
+
});
|
|
39
|
+
const answer = await ask(' Enter numbers separated by commas (e.g. 1,2,3) or "all": ');
|
|
40
|
+
|
|
41
|
+
if (answer.toLowerCase() === 'all' || answer === '') {
|
|
42
|
+
return options.map(o => o.key);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const indices = answer.split(',').map(s => parseInt(s.trim(), 10) - 1);
|
|
46
|
+
return indices
|
|
47
|
+
.filter(i => i >= 0 && i < options.length)
|
|
48
|
+
.map(i => options[i].key);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function copyDir(src, dest, exclude) {
|
|
28
52
|
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
29
53
|
|
|
30
54
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
31
55
|
const srcPath = path.join(src, entry.name);
|
|
32
56
|
const destPath = path.join(dest, entry.name);
|
|
33
57
|
|
|
58
|
+
// Skip template files (handled separately) and excluded patterns
|
|
59
|
+
if (entry.name.endsWith('.template')) continue;
|
|
60
|
+
if (exclude && exclude.some(e => srcPath.includes(e))) continue;
|
|
61
|
+
|
|
34
62
|
if (entry.isDirectory()) {
|
|
35
|
-
copyDir(srcPath, destPath);
|
|
63
|
+
copyDir(srcPath, destPath, exclude);
|
|
36
64
|
} else {
|
|
37
65
|
// Don't overwrite existing files
|
|
38
66
|
if (fs.existsSync(destPath)) {
|
|
@@ -53,6 +81,44 @@ function replaceInFile(filePath, replacements) {
|
|
|
53
81
|
fs.writeFileSync(filePath, content, 'utf8');
|
|
54
82
|
}
|
|
55
83
|
|
|
84
|
+
function hasSyntropicMarker(filePath) {
|
|
85
|
+
if (!fs.existsSync(filePath)) return false;
|
|
86
|
+
return fs.readFileSync(filePath, 'utf8').includes('<!-- syntropic -->');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function renderTemplate(templateFile, replacements) {
|
|
90
|
+
const templatePath = path.join(TEMPLATES_DIR, templateFile);
|
|
91
|
+
if (!fs.existsSync(templatePath)) return null;
|
|
92
|
+
let content = fs.readFileSync(templatePath, 'utf8');
|
|
93
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
94
|
+
content = content.replace(new RegExp(key, 'g'), value);
|
|
95
|
+
}
|
|
96
|
+
return content;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function writeOrAppend(fullTemplate, appendTemplate, destPath, replacements) {
|
|
100
|
+
const relPath = path.relative(process.cwd(), destPath);
|
|
101
|
+
const destDir = path.dirname(destPath);
|
|
102
|
+
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
103
|
+
|
|
104
|
+
if (!fs.existsSync(destPath)) {
|
|
105
|
+
// New file — write full template
|
|
106
|
+
const content = renderTemplate(fullTemplate, replacements);
|
|
107
|
+
if (content) fs.writeFileSync(destPath, content, 'utf8');
|
|
108
|
+
console.log(` create ${relPath}`);
|
|
109
|
+
return 'created';
|
|
110
|
+
} else if (hasSyntropicMarker(destPath)) {
|
|
111
|
+
console.log(` skip ${relPath} (Syntropic rules already present)`);
|
|
112
|
+
return 'skipped';
|
|
113
|
+
} else {
|
|
114
|
+
// Existing file without Syntropic — append methodology
|
|
115
|
+
const appendContent = renderTemplate(appendTemplate, replacements);
|
|
116
|
+
if (appendContent) fs.appendFileSync(destPath, appendContent, 'utf8');
|
|
117
|
+
console.log(` append ${relPath} (added Syntropic pipeline rules)`);
|
|
118
|
+
return 'appended';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
56
122
|
function parseFlags(args) {
|
|
57
123
|
const flags = {};
|
|
58
124
|
const positional = [];
|
|
@@ -67,6 +133,23 @@ function parseFlags(args) {
|
|
|
67
133
|
return { flags, positional };
|
|
68
134
|
}
|
|
69
135
|
|
|
136
|
+
function detectTools(targetDir) {
|
|
137
|
+
const detected = [];
|
|
138
|
+
if (fs.existsSync(path.join(targetDir, 'CLAUDE.md')) || fs.existsSync(path.join(targetDir, '.claude'))) {
|
|
139
|
+
detected.push('claude');
|
|
140
|
+
}
|
|
141
|
+
if (fs.existsSync(path.join(targetDir, '.cursorrules'))) {
|
|
142
|
+
detected.push('cursor');
|
|
143
|
+
}
|
|
144
|
+
if (fs.existsSync(path.join(targetDir, '.windsurfrules'))) {
|
|
145
|
+
detected.push('windsurf');
|
|
146
|
+
}
|
|
147
|
+
if (fs.existsSync(path.join(targetDir, '.github', 'copilot-instructions.md'))) {
|
|
148
|
+
detected.push('copilot');
|
|
149
|
+
}
|
|
150
|
+
return detected;
|
|
151
|
+
}
|
|
152
|
+
|
|
70
153
|
async function run(args) {
|
|
71
154
|
console.log('\n syntropic init — Philosophy-as-code development pipeline\n');
|
|
72
155
|
|
|
@@ -95,20 +178,53 @@ async function run(args) {
|
|
|
95
178
|
const prodUrl = flags['prod-url'] || (isInteractive ? await ask(' Production URL path (e.g. /): ') : '') || '/';
|
|
96
179
|
const prodDomain = flags.domain || (isInteractive ? await ask(' Production domain (e.g. example.com): ') : '') || 'example.com';
|
|
97
180
|
|
|
181
|
+
// Select AI tools
|
|
182
|
+
let selectedTools;
|
|
183
|
+
|
|
184
|
+
if (flags.tools) {
|
|
185
|
+
// --tools claude,cursor,windsurf,copilot
|
|
186
|
+
selectedTools = flags.tools.split(',').map(t => t.trim().toLowerCase());
|
|
187
|
+
} else if (isInteractive) {
|
|
188
|
+
const toolOptions = Object.entries(TOOLS).map(([key, val]) => ({ key, label: val.label }));
|
|
189
|
+
selectedTools = await askMultiSelect('\n Which AI coding tools do you use?', toolOptions);
|
|
190
|
+
if (selectedTools.length === 0) selectedTools = ['claude'];
|
|
191
|
+
} else {
|
|
192
|
+
// Non-interactive default: all tools
|
|
193
|
+
selectedTools = Object.keys(TOOLS);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const replacements = {
|
|
197
|
+
'\\{\\{PROJECT_NAME\\}\\}': projectName,
|
|
198
|
+
'\\{\\{TEST_URL\\}\\}': testUrl,
|
|
199
|
+
'\\{\\{PROD_URL\\}\\}': prodUrl,
|
|
200
|
+
'\\{\\{PROD_DOMAIN\\}\\}': prodDomain,
|
|
201
|
+
};
|
|
202
|
+
|
|
98
203
|
console.log('\n Scaffolding...\n');
|
|
99
204
|
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
205
|
+
// Always exclude CLAUDE.md from copyDir — we handle all instruction files via writeOrAppend
|
|
206
|
+
const exclude = ['CLAUDE.md'];
|
|
207
|
+
if (!selectedTools.includes('claude')) {
|
|
208
|
+
exclude.push('.claude');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Copy shared template files (workflows, scripts, agents)
|
|
212
|
+
copyDir(TEMPLATES_DIR, targetDir, exclude);
|
|
213
|
+
|
|
214
|
+
// Template mapping: [full template, append template]
|
|
215
|
+
const templateMap = {
|
|
216
|
+
claude: ['CLAUDE.md', 'claude-append.template'],
|
|
217
|
+
cursor: ['cursorrules.template', 'tool-append.template'],
|
|
218
|
+
windsurf: ['windsurfrules.template', 'tool-append.template'],
|
|
219
|
+
copilot: ['copilot-instructions.template', 'tool-append.template'],
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Generate instruction files — create new or append to existing
|
|
223
|
+
const actions = {};
|
|
224
|
+
for (const tool of selectedTools) {
|
|
225
|
+
const [fullTpl, appendTpl] = templateMap[tool];
|
|
226
|
+
const destPath = path.join(targetDir, TOOLS[tool].file);
|
|
227
|
+
actions[tool] = writeOrAppend(fullTpl, appendTpl, destPath, replacements);
|
|
112
228
|
}
|
|
113
229
|
|
|
114
230
|
// Make health check executable
|
|
@@ -117,25 +233,30 @@ async function run(args) {
|
|
|
117
233
|
fs.chmodSync(healthScript, '755');
|
|
118
234
|
}
|
|
119
235
|
|
|
120
|
-
//
|
|
236
|
+
// Build summary
|
|
237
|
+
const toolFiles = selectedTools.map(t => {
|
|
238
|
+
const info = TOOLS[t];
|
|
239
|
+
const pad = info.file.padEnd(36);
|
|
240
|
+
return ` ${pad}${info.label} instructions`;
|
|
241
|
+
}).join('\n');
|
|
242
|
+
|
|
243
|
+
const hasAgents = selectedTools.includes('claude');
|
|
244
|
+
|
|
121
245
|
console.log(`
|
|
122
246
|
Done! Your project is set up with the Syntropic pipeline.
|
|
123
247
|
|
|
124
248
|
What was created:
|
|
125
|
-
|
|
126
|
-
.claude/EVERGREEN_RULES.md Full rule reference
|
|
127
|
-
.claude/commands/*.md Generic agents (dev, qa, research, plan, devops, security)
|
|
249
|
+
${toolFiles}${hasAgents ? '\n .claude/EVERGREEN_RULES.md Full rule reference\n .claude/commands/*.md Generic agents (dev, qa, research, plan, devops, security)' : ''}
|
|
128
250
|
.github/workflows/ Daily health check with auto-remediation
|
|
129
251
|
scripts/health-check.js Local health check script
|
|
130
252
|
|
|
253
|
+
Tools configured: ${selectedTools.map(t => TOOLS[t].label).join(', ')}
|
|
254
|
+
|
|
131
255
|
Next steps:
|
|
132
|
-
1. Review
|
|
133
|
-
2.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
3. Start building! Use the EG7 pipeline:
|
|
137
|
-
Full: research -> design -> arch -> plan -> dev -> qa -> test
|
|
138
|
-
Lightweight: research -> plan -> dev -> qa -> test
|
|
256
|
+
1. Review your instruction files and adjust for your project
|
|
257
|
+
2. Start building! Use the EG7 pipeline:
|
|
258
|
+
Full: research -> plan -> dev -> qa -> test
|
|
259
|
+
Lightweight: plan -> dev -> qa -> test
|
|
139
260
|
Minimum: dev -> test
|
|
140
261
|
|
|
141
262
|
The methodology is your superpower. Ship and iterate.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "syntropic",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Philosophy-as-code development pipeline
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Philosophy-as-code development pipeline for Claude Code, Cursor, Windsurf, and GitHub Copilot.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"syntropic": "./bin/syntropic.js"
|
|
7
7
|
},
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"philosophy-as-code",
|
|
12
12
|
"outcome-thinking",
|
|
13
13
|
"claude-code",
|
|
14
|
+
"cursor",
|
|
15
|
+
"windsurf",
|
|
16
|
+
"github-copilot",
|
|
14
17
|
"ai-development",
|
|
15
18
|
"dev-pipeline",
|
|
16
19
|
"syntropic"
|
package/templates/CLAUDE.md
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
<!-- syntropic -->
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Syntropic Development Pipeline
|
|
7
|
+
|
|
8
|
+
Full reference: `.claude/EVERGREEN_RULES.md`
|
|
9
|
+
|
|
10
|
+
### EG7: Implementation Pipeline (MANDATORY)
|
|
11
|
+
Before starting ANY task, state the cycle weight:
|
|
12
|
+
- **Full Cycle** (>3 files or new patterns): `/research` → `/plan` → `/dev` → `/qa`
|
|
13
|
+
- **Lightweight Cycle** (1-3 files, known patterns): `/plan` → `/dev` → `/qa`
|
|
14
|
+
- **Minimum Cycle** (trivial/obvious): `/dev` → verify
|
|
15
|
+
|
|
16
|
+
### EG1: Pre-Flight Loop
|
|
17
|
+
BEFORE deploy: build must pass, no localhost refs. AFTER deploy: verify live URL.
|
|
18
|
+
|
|
19
|
+
### EG2: Ship and Iterate
|
|
20
|
+
Build → Deploy → Show result. Only ask if destructive, expensive, or irreversible.
|
|
21
|
+
|
|
22
|
+
### EG3: Hypothesis-Driven Debugging
|
|
23
|
+
1. State hypothesis 2. Test WITHOUT code changes 3. Only code if confirmed.
|
|
24
|
+
3rd attempt → STOP, deep investigation.
|
|
25
|
+
|
|
26
|
+
### EG8: Live Site Testing
|
|
27
|
+
- Test page: {{TEST_URL}} — all new features go here FIRST
|
|
28
|
+
- Production: {{PROD_URL}} — DO NOT modify without explicit approval
|
|
29
|
+
|
|
30
|
+
### Efficiency (PRISM Methodology)
|
|
31
|
+
- Before complex tasks: estimate cost vs. value
|
|
32
|
+
- After cycles: note what worked and what was wasteful
|
|
33
|
+
- Better output AND fewer resources. Never sacrifice quality to save tokens.
|
|
34
|
+
<!-- /syntropic -->
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<!-- syntropic -->
|
|
2
|
+
# {{PROJECT_NAME}} — Development Instructions
|
|
3
|
+
|
|
4
|
+
These instructions are loaded automatically by GitHub Copilot. Follow them for all work in this project.
|
|
5
|
+
|
|
6
|
+
## Implementation Pipeline (EG7) — MANDATORY
|
|
7
|
+
|
|
8
|
+
Before starting ANY task, identify the cycle weight:
|
|
9
|
+
|
|
10
|
+
- **Full Cycle** (>3 files or new patterns): Research → Plan → Implement → Review
|
|
11
|
+
- **Lightweight Cycle** (1-3 files, known patterns): Plan → Implement → Review
|
|
12
|
+
- **Minimum Cycle** (trivial/obvious): Implement → Verify
|
|
13
|
+
|
|
14
|
+
State the cycle before writing any code. When in doubt, go one level UP.
|
|
15
|
+
|
|
16
|
+
## Core Rules
|
|
17
|
+
|
|
18
|
+
### EG1: Pre-Flight Loop
|
|
19
|
+
BEFORE every deploy: build must pass, no localhost references in source.
|
|
20
|
+
AFTER every deploy: verify the live URL works.
|
|
21
|
+
|
|
22
|
+
### EG2: Ship and Iterate
|
|
23
|
+
Default: Build → Deploy → Show result.
|
|
24
|
+
Only ask if: Destructive, Expensive, or Irreversible.
|
|
25
|
+
|
|
26
|
+
### EG3: Hypothesis-Driven Debugging
|
|
27
|
+
1. State hypothesis ("X because Y")
|
|
28
|
+
2. Test WITHOUT code changes
|
|
29
|
+
3. Only code if confirmed
|
|
30
|
+
4. 3rd attempt same issue → STOP, deep investigation
|
|
31
|
+
|
|
32
|
+
### EG5: No Workarounds Without Approval
|
|
33
|
+
Never implement bypass code, temporary hardcoded values, or "fix later" shortcuts without explicit approval.
|
|
34
|
+
|
|
35
|
+
### EG8: Live Site Testing
|
|
36
|
+
- Test page: {{TEST_URL}} — all new features go here FIRST
|
|
37
|
+
- Production: {{PROD_URL}} — DO NOT modify without explicit approval
|
|
38
|
+
|
|
39
|
+
### EG9: Session Continuity
|
|
40
|
+
On reset: re-read this file, check git status, verify state before proceeding.
|
|
41
|
+
|
|
42
|
+
## Project Details
|
|
43
|
+
|
|
44
|
+
- **Production domain:** {{PROD_DOMAIN}}
|
|
45
|
+
- **Test page:** {{TEST_URL}}
|
|
46
|
+
- **Production page:** {{PROD_URL}}
|
|
47
|
+
|
|
48
|
+
## Efficiency (PRISM Methodology)
|
|
49
|
+
|
|
50
|
+
- Before complex tasks: estimate cost vs. value
|
|
51
|
+
- After cycles: note what worked and what was wasteful
|
|
52
|
+
- Keep: investigation-first for bugs, research-first for features
|
|
53
|
+
- Avoid: jumping to code without context, retrying the same approach
|
|
54
|
+
|
|
55
|
+
## Implementation Standards
|
|
56
|
+
|
|
57
|
+
1. Read before write — always read existing code before modifying
|
|
58
|
+
2. Match existing patterns — follow codebase conventions
|
|
59
|
+
3. Minimal changes — only change what's needed
|
|
60
|
+
4. No over-engineering — solve the current problem only
|
|
61
|
+
5. Security first — no injection vulnerabilities, no hardcoded secrets
|
|
62
|
+
6. Test what you build — verify before marking done
|
|
63
|
+
|
|
64
|
+
## Communication
|
|
65
|
+
|
|
66
|
+
- Be direct and concise
|
|
67
|
+
- Recommend the objectively best solution
|
|
68
|
+
- Own mistakes directly
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<!-- syntropic -->
|
|
2
|
+
# {{PROJECT_NAME}} — Development Rules
|
|
3
|
+
|
|
4
|
+
**These rules are loaded automatically by Cursor. Follow them for all work in this project.**
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## MANDATORY: Implementation Pipeline (EG7)
|
|
9
|
+
|
|
10
|
+
Before starting ANY task, identify the cycle weight:
|
|
11
|
+
|
|
12
|
+
- **Full Cycle** (>3 files or new patterns): Research → Plan → Implement → Review
|
|
13
|
+
- **Lightweight Cycle** (1-3 files, known patterns): Plan → Implement → Review
|
|
14
|
+
- **Minimum Cycle** (trivial/obvious): Implement → Verify
|
|
15
|
+
|
|
16
|
+
State the cycle before writing any code. When in doubt, go one level UP.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Core Rules
|
|
21
|
+
|
|
22
|
+
### EG1: Pre-Flight Loop
|
|
23
|
+
**BEFORE** every deploy:
|
|
24
|
+
- Build must pass (`npm run build` or equivalent)
|
|
25
|
+
- No localhost/127.0.0.1 references in source
|
|
26
|
+
**AFTER** every deploy: verify the live URL works.
|
|
27
|
+
|
|
28
|
+
### EG2: Ship and Iterate
|
|
29
|
+
Default: Build → Deploy → Show result.
|
|
30
|
+
Only ask if: Destructive, Expensive, or Irreversible.
|
|
31
|
+
|
|
32
|
+
### EG3: Hypothesis-Driven Debugging
|
|
33
|
+
1. State hypothesis ("X because Y")
|
|
34
|
+
2. Test WITHOUT code changes (logs, DB, API)
|
|
35
|
+
3. Only code if confirmed
|
|
36
|
+
4. 3rd attempt same issue → STOP, deep investigation
|
|
37
|
+
|
|
38
|
+
### EG5: No Workarounds Without Approval
|
|
39
|
+
Never implement bypass code, temporary hardcoded values, or "fix later" shortcuts without explicit approval.
|
|
40
|
+
|
|
41
|
+
### EG8: Live Site Testing
|
|
42
|
+
- Test page: {{TEST_URL}} — all new features go here FIRST
|
|
43
|
+
- Production: {{PROD_URL}} — DO NOT modify without explicit approval
|
|
44
|
+
- Workflow: Build on test → push → verify live → promote with approval
|
|
45
|
+
|
|
46
|
+
### EG9: Session Continuity
|
|
47
|
+
On session reset: re-read this file, check git status, verify current state before proceeding.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Project: {{PROJECT_NAME}}
|
|
52
|
+
|
|
53
|
+
**Production domain:** {{PROD_DOMAIN}}
|
|
54
|
+
**Test page:** {{TEST_URL}}
|
|
55
|
+
**Production page:** {{PROD_URL}}
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Efficiency (PRISM Methodology)
|
|
60
|
+
|
|
61
|
+
- **Before complex tasks**: estimate cost vs. value
|
|
62
|
+
- **After cycles**: note what worked and what was wasteful
|
|
63
|
+
- **Keep**: investigation-first for bugs, research-first for features
|
|
64
|
+
- **Avoid**: jumping to code without context, retrying the same approach
|
|
65
|
+
|
|
66
|
+
Better output AND fewer resources. Never sacrifice quality to save tokens.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Implementation Standards
|
|
71
|
+
|
|
72
|
+
1. **Read before write** — always read existing code before modifying
|
|
73
|
+
2. **Match existing patterns** — follow codebase conventions
|
|
74
|
+
3. **Minimal changes** — only change what's needed
|
|
75
|
+
4. **No over-engineering** — solve the current problem, not hypothetical future ones
|
|
76
|
+
5. **Security first** — no injection vulnerabilities, no hardcoded secrets
|
|
77
|
+
6. **Test what you build** — verify it works before marking done
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Communication
|
|
82
|
+
|
|
83
|
+
- Be direct and concise
|
|
84
|
+
- Recommend the objectively best solution
|
|
85
|
+
- If you made a mistake, own it directly
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
<!-- syntropic -->
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Syntropic Development Pipeline
|
|
7
|
+
|
|
8
|
+
### Implementation Pipeline (MANDATORY)
|
|
9
|
+
Before starting ANY task, state the cycle weight:
|
|
10
|
+
- **Full Cycle** (>3 files or new patterns): Research → Plan → Implement → Review
|
|
11
|
+
- **Lightweight Cycle** (1-3 files, known patterns): Plan → Implement → Review
|
|
12
|
+
- **Minimum Cycle** (trivial/obvious): Implement → Verify
|
|
13
|
+
|
|
14
|
+
### Pre-Flight Loop
|
|
15
|
+
BEFORE deploy: build must pass, no localhost refs. AFTER deploy: verify live URL.
|
|
16
|
+
|
|
17
|
+
### Ship and Iterate
|
|
18
|
+
Build → Deploy → Show result. Only ask if destructive, expensive, or irreversible.
|
|
19
|
+
|
|
20
|
+
### Hypothesis-Driven Debugging
|
|
21
|
+
1. State hypothesis 2. Test WITHOUT code changes 3. Only code if confirmed.
|
|
22
|
+
3rd attempt → STOP, deep investigation.
|
|
23
|
+
|
|
24
|
+
### Live Site Testing
|
|
25
|
+
- Test page: {{TEST_URL}} — all new features go here FIRST
|
|
26
|
+
- Production: {{PROD_URL}} — DO NOT modify without explicit approval
|
|
27
|
+
|
|
28
|
+
### Efficiency (PRISM Methodology)
|
|
29
|
+
- Before complex tasks: estimate cost vs. value
|
|
30
|
+
- After cycles: note what worked and what was wasteful
|
|
31
|
+
- Better output AND fewer resources. Never sacrifice quality to save tokens.
|
|
32
|
+
<!-- /syntropic -->
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<!-- syntropic -->
|
|
2
|
+
# {{PROJECT_NAME}} — Development Rules
|
|
3
|
+
|
|
4
|
+
**These rules are loaded automatically by Windsurf. Follow them for all work in this project.**
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## MANDATORY: Implementation Pipeline (EG7)
|
|
9
|
+
|
|
10
|
+
Before starting ANY task, identify the cycle weight:
|
|
11
|
+
|
|
12
|
+
- **Full Cycle** (>3 files or new patterns): Research → Plan → Implement → Review
|
|
13
|
+
- **Lightweight Cycle** (1-3 files, known patterns): Plan → Implement → Review
|
|
14
|
+
- **Minimum Cycle** (trivial/obvious): Implement → Verify
|
|
15
|
+
|
|
16
|
+
State the cycle before writing any code. When in doubt, go one level UP.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Core Rules
|
|
21
|
+
|
|
22
|
+
### EG1: Pre-Flight Loop
|
|
23
|
+
**BEFORE** every deploy:
|
|
24
|
+
- Build must pass (`npm run build` or equivalent)
|
|
25
|
+
- No localhost/127.0.0.1 references in source
|
|
26
|
+
**AFTER** every deploy: verify the live URL works.
|
|
27
|
+
|
|
28
|
+
### EG2: Ship and Iterate
|
|
29
|
+
Default: Build → Deploy → Show result.
|
|
30
|
+
Only ask if: Destructive, Expensive, or Irreversible.
|
|
31
|
+
|
|
32
|
+
### EG3: Hypothesis-Driven Debugging
|
|
33
|
+
1. State hypothesis ("X because Y")
|
|
34
|
+
2. Test WITHOUT code changes (logs, DB, API)
|
|
35
|
+
3. Only code if confirmed
|
|
36
|
+
4. 3rd attempt same issue → STOP, deep investigation
|
|
37
|
+
|
|
38
|
+
### EG5: No Workarounds Without Approval
|
|
39
|
+
Never implement bypass code, temporary hardcoded values, or "fix later" shortcuts without explicit approval.
|
|
40
|
+
|
|
41
|
+
### EG8: Live Site Testing
|
|
42
|
+
- Test page: {{TEST_URL}} — all new features go here FIRST
|
|
43
|
+
- Production: {{PROD_URL}} — DO NOT modify without explicit approval
|
|
44
|
+
- Workflow: Build on test → push → verify live → promote with approval
|
|
45
|
+
|
|
46
|
+
### EG9: Session Continuity
|
|
47
|
+
On session reset: re-read this file, check git status, verify current state before proceeding.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Project: {{PROJECT_NAME}}
|
|
52
|
+
|
|
53
|
+
**Production domain:** {{PROD_DOMAIN}}
|
|
54
|
+
**Test page:** {{TEST_URL}}
|
|
55
|
+
**Production page:** {{PROD_URL}}
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Efficiency (PRISM Methodology)
|
|
60
|
+
|
|
61
|
+
- **Before complex tasks**: estimate cost vs. value
|
|
62
|
+
- **After cycles**: note what worked and what was wasteful
|
|
63
|
+
- **Keep**: investigation-first for bugs, research-first for features
|
|
64
|
+
- **Avoid**: jumping to code without context, retrying the same approach
|
|
65
|
+
|
|
66
|
+
Better output AND fewer resources. Never sacrifice quality to save tokens.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Implementation Standards
|
|
71
|
+
|
|
72
|
+
1. **Read before write** — always read existing code before modifying
|
|
73
|
+
2. **Match existing patterns** — follow codebase conventions
|
|
74
|
+
3. **Minimal changes** — only change what's needed
|
|
75
|
+
4. **No over-engineering** — solve the current problem, not hypothetical future ones
|
|
76
|
+
5. **Security first** — no injection vulnerabilities, no hardcoded secrets
|
|
77
|
+
6. **Test what you build** — verify it works before marking done
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Communication
|
|
82
|
+
|
|
83
|
+
- Be direct and concise
|
|
84
|
+
- Recommend the objectively best solution
|
|
85
|
+
- If you made a mistake, own it directly
|