cc-starter 1.0.7 → 1.1.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 CHANGED
@@ -32,6 +32,7 @@ One command. Interactive wizard. Full setup in 30 seconds.
32
32
  | `scripts/stats/vibe-stats.js` | Track and measure your token savings |
33
33
  | `scripts/stats/cocomo.js` | COCOMO-II project cost estimation |
34
34
  | `scripts/stats/project-report.js` | Visual HTML statistics report |
35
+ | AI Operating System | Optional developer profile — role, experience, communication style |
35
36
  | Plugin presets | One-click installation of best Claude Code plugins |
36
37
 
37
38
  ---
@@ -92,6 +93,14 @@ Plugins are installed via `claude mcp add` — no manual config needed.
92
93
 
93
94
  ---
94
95
 
96
+ ## 🧠 AI Operating System (optional)
97
+
98
+ Personalize Claude to your working style. The wizard asks for your role, experience level, and communication preference — then auto-derives smart defaults (no beginner explanations for seniors, architecture focus for tech leads, etc.).
99
+
100
+ Saved as `.claude/memory/user_profile.md` — not committed to git.
101
+
102
+ ---
103
+
95
104
  ## 🚀 Installation
96
105
 
97
106
  ```bash
@@ -120,6 +129,7 @@ npx cc-starter
120
129
  | Memory system | ✅ | ❌ | ❌ | ❌ |
121
130
  | Tech stack detection | ✅ | ❌ | ❌ | ✅ |
122
131
  | Zero dependencies in output | ✅ | ✅ | ❌ | ✅ |
132
+ | AI Operating System | ✅ | ❌ | ❌ | ❌ |
123
133
 
124
134
  ---
125
135
 
@@ -129,6 +139,7 @@ npx cc-starter
129
139
  npx cc-starter
130
140
  ├─ Detect tech stack (TypeScript, Next.js, ...)
131
141
  ├─ Interactive wizard (name, rate, plugins)
142
+ ├─ Optional: personalize Claude (role, style)
132
143
  ├─ Scaffold .claude/ + scripts/ + CLAUDE.md
133
144
  ├─ Install selected plugins
134
145
  └─ Ready to code!
package/lib/constants.js CHANGED
@@ -26,6 +26,30 @@ export const ALL_PLUGINS = [
26
26
  { name: 'claude-seo', source: 'claude-seo', desc: 'SEO audits, E-E-A-T analysis, schema markup, Core Web Vitals, AI search optimization' }
27
27
  ];
28
28
 
29
+ export const ROLES = [
30
+ { value: 'frontend', en: 'Frontend Developer', de: 'Frontend Developer' },
31
+ { value: 'backend', en: 'Backend Developer', de: 'Backend Developer' },
32
+ { value: 'fullstack', en: 'Fullstack Developer', de: 'Fullstack Developer' },
33
+ { value: 'techlead', en: 'Tech Lead / Architect', de: 'Tech Lead / Architect' },
34
+ { value: 'devops', en: 'DevOps / SRE', de: 'DevOps / SRE' },
35
+ { value: 'data', en: 'Data Scientist / ML Engineer', de: 'Data Scientist / ML Engineer' },
36
+ { value: 'student', en: 'Student / Learning', de: 'Student / Lernend' },
37
+ { value: 'other', en: 'Other', de: 'Andere' }
38
+ ];
39
+
40
+ export const EXPERIENCE_LEVELS = [
41
+ { value: 'beginner', en: 'Beginner (< 1 year)', de: 'Einsteiger (< 1 Jahr)' },
42
+ { value: 'intermediate', en: 'Intermediate (1-3 years)', de: 'Fortgeschritten (1-3 Jahre)' },
43
+ { value: 'experienced', en: 'Experienced (3-7 years)', de: 'Erfahren (3-7 Jahre)' },
44
+ { value: 'senior', en: 'Senior (7+ years)', de: 'Senior (7+ Jahre)' }
45
+ ];
46
+
47
+ export const COMM_STYLES = [
48
+ { value: 'short', en: 'Short & direct — code and results, minimal explanations', de: 'Kurz & direkt — Code und Ergebnis, minimale Erklärungen' },
49
+ { value: 'balanced', en: 'Balanced — brief explanations when needed', de: 'Balanced — kurze Erklärungen wenn nötig' },
50
+ { value: 'detailed', en: 'Detailed — explain decisions and alternatives', de: 'Ausführlich — Entscheidungen und Alternativen erklären' }
51
+ ];
52
+
29
53
  export const REPORT_STYLES = {
30
54
  minimal: { label: 'Minimal — zero dependencies, plain HTML with CSS bars', packages: [] },
31
55
  fancy: { label: 'Fancy — visual charts via chart.js CDN (no npm install needed)', packages: [] }
@@ -45,7 +69,12 @@ export const I18N = {
45
69
  pluginPreset: 'Plugin-Auswahl:',
46
70
  selectPlugins: 'Plugins auswählen:',
47
71
  detectedStack: 'Erkannter Tech-Stack: ',
48
- custom: 'Individuell — einzelne Plugins wählen'
72
+ custom: 'Individuell — einzelne Plugins wählen',
73
+ personalize: 'Claude personalisieren?',
74
+ role: 'Deine Rolle:',
75
+ specialization: 'Spezialisierung / Schwerpunkt (optional, max 100 Zeichen):',
76
+ experienceLevel: 'Erfahrungslevel:',
77
+ commStyle: 'Kommunikationsstil:'
49
78
  },
50
79
  scaffold: {
51
80
  scaffolding: 'Projekt wird eingerichtet...',
@@ -82,6 +111,27 @@ export const I18N = {
82
111
  costEstimate: 'Projekt-Kostenschätzung',
83
112
  tokenTools: 'Token-sparende Tools',
84
113
  htmlStats: 'HTML-Statistiken'
114
+ },
115
+ profile: {
116
+ title: 'Personalisierung',
117
+ headings: {
118
+ role: '## Rolle',
119
+ experience: '## Erfahrung',
120
+ commStyle: '## Kommunikationsstil',
121
+ preferences: '## Präferenzen'
122
+ },
123
+ preferences: {
124
+ noBeginnerExplanations: 'Keine Anfänger-Erklärungen bei Standardkonzepten',
125
+ noBasicsRepetition: 'Keine Wiederholung von Grundlagen, erwarte eigenständiges Urteil',
126
+ noSummaries: 'Keine Zusammenfassungen am Ende, keine Einleitungen',
127
+ showAlternatives: 'Alternativen und Trade-offs immer mitliefern',
128
+ explainConcepts: 'Konzepte erklären, Lernressourcen vorschlagen',
129
+ architectureFocus: 'Architektur-Perspektive priorisieren, Team-Impact berücksichtigen',
130
+ infraFocus: 'Infrastruktur, Sicherheit und Monitoring mitdenken'
131
+ },
132
+ existingProfile: 'Vorhandenes Profil gefunden. Was soll passieren?',
133
+ overwriteProfile: 'Überschreiben',
134
+ skipProfile: 'Überspringen'
85
135
  }
86
136
  },
87
137
  en: {
@@ -95,7 +145,12 @@ export const I18N = {
95
145
  pluginPreset: 'Plugin preset:',
96
146
  selectPlugins: 'Select plugins:',
97
147
  detectedStack: 'Detected tech stack: ',
98
- custom: 'Custom — pick individual plugins'
148
+ custom: 'Custom — pick individual plugins',
149
+ personalize: 'Personalize Claude?',
150
+ role: 'Your role:',
151
+ specialization: 'Specialization / focus (optional, max 100 chars):',
152
+ experienceLevel: 'Experience level:',
153
+ commStyle: 'Communication style:'
99
154
  },
100
155
  scaffold: {
101
156
  scaffolding: 'Scaffolding project...',
@@ -132,6 +187,61 @@ export const I18N = {
132
187
  costEstimate: 'Project cost estimate',
133
188
  tokenTools: 'Token-saving tools',
134
189
  htmlStats: 'HTML statistics'
190
+ },
191
+ profile: {
192
+ title: 'Personalization',
193
+ headings: {
194
+ role: '## Role',
195
+ experience: '## Experience',
196
+ commStyle: '## Communication Style',
197
+ preferences: '## Preferences'
198
+ },
199
+ preferences: {
200
+ noBeginnerExplanations: 'No beginner explanations for standard concepts',
201
+ noBasicsRepetition: 'No repetition of basics, expect independent judgment',
202
+ noSummaries: 'No summaries at the end, no introductions',
203
+ showAlternatives: 'Always provide alternatives and trade-offs',
204
+ explainConcepts: 'Explain concepts, suggest learning resources',
205
+ architectureFocus: 'Prioritize architecture perspective, consider team impact',
206
+ infraFocus: 'Think about infrastructure, security, and monitoring'
207
+ },
208
+ existingProfile: 'Existing profile found. What should we do?',
209
+ overwriteProfile: 'Overwrite',
210
+ skipProfile: 'Skip'
135
211
  }
136
212
  }
137
213
  };
214
+
215
+ export function derivePreferences(role, level, commStyle, lang) {
216
+ const prefs = [];
217
+ const t = I18N[lang]?.profile?.preferences || I18N.en.profile.preferences;
218
+
219
+ // Level-based
220
+ if (level === 'experienced' || level === 'senior') {
221
+ prefs.push(t.noBeginnerExplanations);
222
+ }
223
+ if (level === 'senior') {
224
+ prefs.push(t.noBasicsRepetition);
225
+ }
226
+
227
+ // Communication style
228
+ if (commStyle === 'short') {
229
+ prefs.push(t.noSummaries);
230
+ }
231
+ if (commStyle === 'detailed') {
232
+ prefs.push(t.showAlternatives);
233
+ }
234
+
235
+ // Role-based
236
+ if (role === 'student') {
237
+ prefs.push(t.explainConcepts);
238
+ }
239
+ if (role === 'techlead') {
240
+ prefs.push(t.architectureFocus);
241
+ }
242
+ if (role === 'devops') {
243
+ prefs.push(t.infraFocus);
244
+ }
245
+
246
+ return prefs;
247
+ }
package/lib/scaffold.js CHANGED
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'url';
4
4
  import Handlebars from 'handlebars';
5
5
  import inquirer from 'inquirer';
6
6
  import chalk from 'chalk';
7
- import { I18N } from './constants.js';
7
+ import { I18N, derivePreferences, ROLES, EXPERIENCE_LEVELS, COMM_STYLES } from './constants.js';
8
8
 
9
9
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
10
  const TEMPLATE_DIR = path.join(__dirname, '..', 'template');
@@ -95,6 +95,12 @@ export async function scaffold(config, cwd = process.cwd()) {
95
95
  filesCreated += 1;
96
96
  console.log(chalk.green(' ✓ .cc-starter.json') + chalk.dim(` ${t.scaffold.created}`));
97
97
 
98
+ // ── 4b. Write user profile (if personalization was chosen) ────────
99
+ const profileResult = await writeUserProfile(config, claudeAction, cwd, lang, t);
100
+ if (profileResult.created) {
101
+ filesCreated += 1;
102
+ }
103
+
98
104
  // ── 5. Update .gitignore ───────────────────────────────────────────
99
105
  const gitignoreUpdated = updateGitignore(cwd);
100
106
  if (gitignoreUpdated) {
@@ -239,6 +245,16 @@ function updateGitignore(cwd) {
239
245
 
240
246
  const marker = '# cc-starter';
241
247
  if (content.includes(marker)) {
248
+ // Block exists — check if user_profile.md entry is present (upgrade path)
249
+ const profileEntry = '.claude/memory/user_profile.md';
250
+ if (!content.includes(profileEntry)) {
251
+ const updatedContent = content.replace(
252
+ 'stats/report.html',
253
+ 'stats/report.html\n' + profileEntry
254
+ );
255
+ fs.writeFileSync(gitignorePath, updatedContent, 'utf-8');
256
+ return true;
257
+ }
242
258
  return false;
243
259
  }
244
260
 
@@ -247,6 +263,7 @@ function updateGitignore(cwd) {
247
263
  '# cc-starter',
248
264
  '.vibe-stats.json',
249
265
  'stats/report.html',
266
+ '.claude/memory/user_profile.md',
250
267
  ''
251
268
  ].join('\n');
252
269
 
@@ -256,6 +273,93 @@ function updateGitignore(cwd) {
256
273
  return true;
257
274
  }
258
275
 
276
+ /**
277
+ * Write .claude/memory/user_profile.md from profile config.
278
+ * Skipped if claudeAction is 'skip'.
279
+ * @returns {{ created: boolean }}
280
+ */
281
+ async function writeUserProfile(config, claudeAction, cwd, lang, t) {
282
+ if (!config.profile || claudeAction === 'skip') {
283
+ return { created: false };
284
+ }
285
+
286
+ const profilePath = path.join(cwd, '.claude', 'memory', 'user_profile.md');
287
+ const profileExists = fs.existsSync(profilePath);
288
+
289
+ if (profileExists) {
290
+ const answer = await inquirer.prompt([{
291
+ type: 'list',
292
+ name: 'action',
293
+ message: t.profile.existingProfile,
294
+ choices: [
295
+ { name: t.profile.overwriteProfile, value: 'overwrite' },
296
+ { name: t.profile.skipProfile, value: 'skip' }
297
+ ]
298
+ }]);
299
+ if (answer.action === 'skip') {
300
+ console.log(chalk.yellow(' ⊘ user_profile.md') + chalk.dim(` ${t.scaffold.skipped}`));
301
+ return { created: false };
302
+ }
303
+ }
304
+
305
+ const { role, specialization, level, commStyle } = config.profile;
306
+ const preferences = derivePreferences(role, level, commStyle, lang);
307
+ const h = t.profile.headings;
308
+
309
+ // Find display labels
310
+ const roleObj = ROLES.find(r => r.value === role);
311
+ const levelObj = EXPERIENCE_LEVELS.find(l => l.value === level);
312
+ const commObj = COMM_STYLES.find(c => c.value === commStyle);
313
+
314
+ const roleLabel = roleObj ? roleObj[lang] : role;
315
+ const levelLabel = levelObj ? levelObj[lang] : level;
316
+ const commLabel = commObj ? commObj[lang] : commStyle;
317
+
318
+ const roleLine = specialization ? `${roleLabel} — ${specialization}` : roleLabel;
319
+
320
+ const lines = [
321
+ '---',
322
+ 'name: User Profile',
323
+ 'description: Role, experience level, communication preferences and auto-derived guidelines',
324
+ 'type: user',
325
+ '---',
326
+ '',
327
+ h.role,
328
+ roleLine,
329
+ '',
330
+ h.experience,
331
+ levelLabel,
332
+ '',
333
+ h.commStyle,
334
+ commLabel,
335
+ ];
336
+
337
+ if (preferences.length > 0) {
338
+ lines.push('', h.preferences);
339
+ for (const pref of preferences) {
340
+ lines.push(`- ${pref}`);
341
+ }
342
+ }
343
+
344
+ lines.push('');
345
+
346
+ fs.ensureDirSync(path.join(cwd, '.claude', 'memory'));
347
+ fs.writeFileSync(profilePath, lines.join('\n'), 'utf-8');
348
+ console.log(chalk.green(' ✓ user_profile.md') + chalk.dim(` ${t.scaffold.created}`));
349
+
350
+ // Append to MEMORY.md if not already present (idempotency guard)
351
+ const memoryPath = path.join(cwd, '.claude', 'memory', 'MEMORY.md');
352
+ if (fs.existsSync(memoryPath)) {
353
+ const memoryContent = fs.readFileSync(memoryPath, 'utf-8');
354
+ if (!memoryContent.includes('user_profile.md')) {
355
+ const entry = '\n- [User Profile](user_profile.md) — Role, experience, communication preferences\n';
356
+ fs.appendFileSync(memoryPath, entry, 'utf-8');
357
+ }
358
+ }
359
+
360
+ return { created: true };
361
+ }
362
+
259
363
  /**
260
364
  * Count files recursively in a directory.
261
365
  */
package/lib/wizard.js CHANGED
@@ -7,7 +7,10 @@ import {
7
7
  ALL_PLUGINS,
8
8
  REPORT_STYLES,
9
9
  DEFAULT_HOURLY_RATE,
10
- I18N
10
+ I18N,
11
+ ROLES,
12
+ EXPERIENCE_LEVELS,
13
+ COMM_STYLES
11
14
  } from './constants.js';
12
15
 
13
16
  /**
@@ -26,7 +29,7 @@ function detectProjectName() {
26
29
  /**
27
30
  * Interactive setup wizard.
28
31
  * @param {string[]} techStack - Auto-detected technologies (e.g. ['Node.js', 'TypeScript', 'React'])
29
- * @returns {Promise<{projectName: string, hourlyRate: number, reportStyle: string, plugins: object[], techStack: string[], lang: string}>}
32
+ * @returns {Promise<{projectName: string, hourlyRate: number, reportStyle: string, plugins: object[], techStack: string[], lang: string, profile?: {role: string, specialization: string, level: string, commStyle: string}}>}
30
33
  */
31
34
  export async function wizard(techStack = []) {
32
35
  // Language selection FIRST — before anything else
@@ -111,6 +114,56 @@ export async function wizard(techStack = []) {
111
114
  ? answers.customPlugins || []
112
115
  : PLUGIN_PRESETS[answers.pluginPreset];
113
116
 
117
+ // ── Optional personalization ──────────────────────────────────────
118
+ let profile = null;
119
+
120
+ const { wantsProfile } = await inquirer.prompt([{
121
+ type: 'confirm',
122
+ name: 'wantsProfile',
123
+ message: t.wizard.personalize,
124
+ default: false
125
+ }]);
126
+
127
+ if (wantsProfile) {
128
+ console.log();
129
+ console.log(chalk.bold.cyan(' ' + t.profile.title));
130
+ console.log();
131
+
132
+ const profileAnswers = await inquirer.prompt([
133
+ {
134
+ type: 'list',
135
+ name: 'role',
136
+ message: t.wizard.role,
137
+ choices: ROLES.map(r => ({ name: r[lang], value: r.value }))
138
+ },
139
+ {
140
+ type: 'input',
141
+ name: 'specialization',
142
+ message: t.wizard.specialization,
143
+ validate: (input) => input.length <= 100 || (lang === 'de' ? 'Max. 100 Zeichen' : 'Max 100 characters')
144
+ },
145
+ {
146
+ type: 'list',
147
+ name: 'level',
148
+ message: t.wizard.experienceLevel,
149
+ choices: EXPERIENCE_LEVELS.map(l => ({ name: l[lang], value: l.value }))
150
+ },
151
+ {
152
+ type: 'list',
153
+ name: 'commStyle',
154
+ message: t.wizard.commStyle,
155
+ choices: COMM_STYLES.map(c => ({ name: c[lang], value: c.value }))
156
+ }
157
+ ]);
158
+
159
+ profile = {
160
+ role: profileAnswers.role,
161
+ specialization: profileAnswers.specialization || '',
162
+ level: profileAnswers.level,
163
+ commStyle: profileAnswers.commStyle
164
+ };
165
+ }
166
+
114
167
  return {
115
168
  projectName: answers.projectName,
116
169
  projectDescription: answers.projectDescription || '',
@@ -118,6 +171,7 @@ export async function wizard(techStack = []) {
118
171
  reportStyle: answers.reportStyle,
119
172
  plugins,
120
173
  techStack,
121
- lang
174
+ lang,
175
+ profile
122
176
  };
123
177
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-starter",
3
- "version": "1.0.7",
3
+ "version": "1.1.0",
4
4
  "description": "Claude Code Project Kickstart — scaffolds an optimized dev environment with token-saving scripts, COCOMO estimation, and interactive plugin setup",
5
5
  "type": "module",
6
6
  "bin": {