k-harness 0.8.3 → 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 CHANGED
@@ -39,7 +39,6 @@ npx k-harness init --ide claude
39
39
  npx k-harness init --ide cursor
40
40
  npx k-harness init --ide codex
41
41
  npx k-harness init --ide windsurf
42
- npx k-harness init --ide augment
43
42
  npx k-harness init --ide antigravity
44
43
  ```
45
44
 
@@ -47,7 +46,7 @@ npx k-harness init --ide antigravity
47
46
 
48
47
  | Flag | Description |
49
48
  |------|-------------|
50
- | `--ide <name>` | Target IDE: `vscode`, `claude`, `cursor`, `codex`, `windsurf`, `augment`, `antigravity` |
49
+ | `--ide <name>` | Target IDE: `vscode`, `claude`, `cursor`, `codex`, `windsurf`, `antigravity` |
51
50
  | `--dir <path>` | Target directory (default: current directory) |
52
51
  | `--overwrite` | Overwrite existing files |
53
52
 
@@ -58,9 +57,8 @@ npx k-harness init --ide antigravity
58
57
  | **VS Code Copilot** | `.github/copilot-instructions.md` | `.github/skills/*/SKILL.md` | `.github/agents/*.agent.md` |
59
58
  | **Claude Code** | `.claude/rules/core.md` | `.claude/skills/*/SKILL.md` | `.claude/skills/*/SKILL.md` |
60
59
  | **Cursor** | `.cursor/rules/core.mdc` | `.cursor/skills/*/SKILL.md` | `.cursor/skills/*/SKILL.md` |
61
- | **Codex** | `AGENTS.md` | `.agents/skills/*/SKILL.md` | (merged into AGENTS.md) |
62
- | **Windsurf** | `.windsurfrules` | (merged) | (merged) |
63
- | **Augment Code** | `.augment/rules/core.md` | `.augment/skills/*/SKILL.md` | `.augment/skills/*/SKILL.md` |
60
+ | **Codex** | `AGENTS.md` | `.agents/skills/*/SKILL.md` | `.agents/skills/*/SKILL.md` |
61
+ | **Windsurf** | `.windsurf/rules/core.md` | `.windsurf/skills/*/SKILL.md` | `.windsurf/skills/*/SKILL.md` |
64
62
  | **Google Antigravity** | `.agent/rules/core.md` | `.agent/skills/*/SKILL.md` | `.agent/skills/*/SKILL.md` |
65
63
 
66
64
  All IDEs also get state files (`project-state.md`, `project-brief.md`, `features.md`, `failure-patterns.md`, `dependency-map.md`) in the `docs/` directory.
@@ -129,7 +127,7 @@ See [docs/reference.md](docs/reference.md) for detailed descriptions of every sk
129
127
  | Focus | Enterprise SDLC methodology | 1-person software factory | Full lifecycle automation | Project direction management |
130
128
  | Files | 200+ | ~40 | Hundreds | ~20 |
131
129
  | Dependencies | Node 20+ | Bun + Node + Playwright | Node 18+ | Zero |
132
- | IDE support | 20+ (installer) | 5 (setup --host) | 13 (runtime select) | 7 (native format) |
130
+ | IDE support | 20+ (installer) | 5 (setup --host) | 13 (runtime select) | 6 (native format) |
133
131
  | Direction management | ❌ | ❌ | ❌ | ✅ (Direction Guard + pivot + Decision Log) |
134
132
  | Iron Laws (code quality rules) | ❌ | ❌ | ❌ | ✅ (6 laws embedded in skills) |
135
133
  | Cold start | ❌ | ❌ | `/gsd-new-project` | ✅ (`bootstrap` skill) |
@@ -12,6 +12,7 @@ A living document of module relationships. Update whenever modules are added or
12
12
  | services | application | Business logic | auth, database | api |
13
13
  | database | infrastructure| Data persistence | - | services |
14
14
  -->
15
+ <!-- Add new modules above this line -->
15
16
 
16
17
  ## Dependency Rules
17
18
 
@@ -13,6 +13,7 @@ This is LLM's map to understand "what exists" — without this, features get for
13
13
  | Admin Dashboard | ⬜ planned | - | - | - |
14
14
  | Legacy REST API | ❌ dropped | - | - | - |
15
15
  -->
16
+ <!-- Add new features above this line -->
16
17
 
17
18
  ## Status Legend
18
19
 
@@ -100,3 +100,16 @@ Add an entry to the Decision Log section in docs/project-brief.md:
100
100
  - **Never update partially** — if you update docs/project-brief.md, you MUST check and update all other state files too
101
101
  - **Preserve history** — mark dropped features as `⛔ dropped`, don't delete rows
102
102
  - **Record the why** — every pivot must have a Decision Log entry with reasoning
103
+
104
+ ## Team Mode
105
+
106
+ If `.harness/` directory exists (Team mode is active):
107
+
108
+ - **pivot MUST be run on the main branch** by the team lead or architect only
109
+ - Feature branches should NOT run pivot independently
110
+ - If a direction change is needed from a feature branch:
111
+ 1. Document the proposed change
112
+ 2. Share with the team
113
+ 3. Team lead runs pivot on main
114
+ 4. All developers pull main to get updated shared state files
115
+ 5. Each developer's `.harness/` personal state is unaffected (update manually if needed)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "k-harness",
3
- "version": "0.8.3",
3
+ "version": "0.9.0",
4
4
  "description": "LLM Development Harness — IDE-agnostic rules, skills, and agents that prevent common AI coding failures",
5
5
  "keywords": [
6
6
  "llm",
package/src/init.js CHANGED
@@ -57,7 +57,43 @@ const AGENT_MEMORY_FILES = [
57
57
  'agent-memory/sprint-manager.md',
58
58
  ];
59
59
 
60
+ const PERSONAL_STATE_FILES = ['project-state.md', 'failure-patterns.md'];
61
+ const PERSONAL_DIRS = ['agent-memory/'];
62
+
60
63
  const STATE_DEST_DIR = 'docs';
64
+ const PERSONAL_DEST_DIR = '.harness';
65
+
66
+ // ─── Team mode path resolver ─────────────────────────────────
67
+ const TEAM_MODE_SECTION = `
68
+
69
+ ## Team Mode
70
+
71
+ This project uses Team mode. State files are split into shared and personal.
72
+
73
+ ### File Locations
74
+ - **Shared** (docs/, git committed): project-brief.md, features.md, dependency-map.md
75
+ - **Personal** (.harness/, gitignored): project-state.md, failure-patterns.md, agent-memory/
76
+
77
+ ### Rules
78
+ 1. Shared files: only modify your own rows (check Owner column)
79
+ 2. Other developers' Owner rows are READ ONLY
80
+ 3. New rows go at the bottom of the table
81
+ 4. The \`pivot\` skill must be run on the main branch by the team lead only
82
+ `;
83
+
84
+ function resolveContent(content, mode) {
85
+ if (mode !== 'team') return content;
86
+ let result = content
87
+ .replaceAll('docs/project-state.md', '.harness/project-state.md')
88
+ .replaceAll('docs/failure-patterns.md', '.harness/failure-patterns.md')
89
+ .replaceAll('docs/agent-memory/', '.harness/agent-memory/');
90
+
91
+ // Append Team Mode section to core-rules (detected by the heading)
92
+ if (result.includes('## State Files') && result.includes('## Session Start')) {
93
+ result += TEAM_MODE_SECTION;
94
+ }
95
+ return result;
96
+ }
61
97
 
62
98
  // ─── Language detection ──────────────────────────────────────
63
99
  function detectLanguage(targetDir) {
@@ -80,18 +116,22 @@ function detectLanguage(targetDir) {
80
116
 
81
117
  // ─── Shared writers ──────────────────────────────────────────
82
118
 
83
- function writeStateFiles(targetDir, overwrite) {
119
+ function writeStateFiles(targetDir, overwrite, mode = 'solo') {
84
120
  for (const file of STATE_FILES) {
85
- writeFile(targetDir, `${STATE_DEST_DIR}/${file}`, readTemplate(file), overwrite);
121
+ const isPersonal = PERSONAL_STATE_FILES.includes(file);
122
+ const destDir = (mode === 'team' && isPersonal) ? PERSONAL_DEST_DIR : STATE_DEST_DIR;
123
+ const content = resolveContent(readTemplate(file), mode);
124
+ writeFile(targetDir, `${destDir}/${file}`, content, overwrite);
86
125
  }
87
126
  for (const file of AGENT_MEMORY_FILES) {
88
- writeFile(targetDir, `${STATE_DEST_DIR}/${file}`, readTemplate(file), overwrite);
127
+ const destDir = mode === 'team' ? PERSONAL_DEST_DIR : STATE_DEST_DIR;
128
+ writeFile(targetDir, `${destDir}/${file}`, readTemplate(file), overwrite);
89
129
  }
90
130
  }
91
131
 
92
- function writeSkills(targetDir, skillsDir, overwrite) {
132
+ function writeSkills(targetDir, skillsDir, overwrite, mode = 'solo') {
93
133
  for (const skill of SKILLS) {
94
- const content = readTemplate(`skills/${skill.id}.md`);
134
+ const content = resolveContent(readTemplate(`skills/${skill.id}.md`), mode);
95
135
  const skillMd =
96
136
  `---\nname: ${skill.id}\ndescription: '${skill.desc}'\n---\n\n` +
97
137
  content;
@@ -99,9 +139,9 @@ function writeSkills(targetDir, skillsDir, overwrite) {
99
139
  }
100
140
  }
101
141
 
102
- function writeAgentsAsSkills(targetDir, skillsDir, overwrite) {
142
+ function writeAgentsAsSkills(targetDir, skillsDir, overwrite, mode = 'solo') {
103
143
  for (const agent of AGENTS) {
104
- const content = readTemplate(agent.file);
144
+ const content = resolveContent(readTemplate(agent.file), mode);
105
145
  const skillMd =
106
146
  `---\nname: ${agent.id}\ndescription: '${agent.desc}'\n---\n\n` +
107
147
  content;
@@ -111,18 +151,18 @@ function writeAgentsAsSkills(targetDir, skillsDir, overwrite) {
111
151
 
112
152
  // ─── IDE Generators ──────────────────────────────────────────
113
153
 
114
- function generateVscode(targetDir, overwrite) {
115
- const coreRules = readTemplate('core-rules.md');
154
+ function generateVscode(targetDir, overwrite, mode = 'solo') {
155
+ const coreRules = resolveContent(readTemplate('core-rules.md'), mode);
116
156
 
117
157
  // Global instructions (dispatcher only — rules are embedded in skills)
118
158
  writeFile(targetDir, '.github/copilot-instructions.md', coreRules, overwrite);
119
159
 
120
160
  // Skills (.github/skills — VS Code default search path, SKILL.md with frontmatter)
121
- writeSkills(targetDir, '.github/skills', overwrite);
161
+ writeSkills(targetDir, '.github/skills', overwrite, mode);
122
162
 
123
163
  // Agents (.github/agents — VS Code uses .agent.md format with frontmatter)
124
164
  for (const agent of AGENTS) {
125
- const content = readTemplate(agent.file);
165
+ const content = resolveContent(readTemplate(agent.file), mode);
126
166
  const agentMd =
127
167
  `---\nname: ${agent.id}\ndescription: "${agent.desc}"\n---\n\n` +
128
168
  content;
@@ -130,90 +170,87 @@ function generateVscode(targetDir, overwrite) {
130
170
  }
131
171
 
132
172
  // State files
133
- writeStateFiles(targetDir, overwrite);
173
+ writeStateFiles(targetDir, overwrite, mode);
134
174
  }
135
175
 
136
- function generateClaude(targetDir, overwrite) {
176
+ function generateClaude(targetDir, overwrite, mode = 'solo') {
137
177
  // .claude/rules/core.md — dispatcher only (no paths = always loaded)
138
- writeFile(targetDir, '.claude/rules/core.md', readTemplate('core-rules.md'), overwrite);
178
+ writeFile(targetDir, '.claude/rules/core.md', resolveContent(readTemplate('core-rules.md'), mode), overwrite);
139
179
 
140
180
  // Skills (SKILL.md with frontmatter)
141
- writeSkills(targetDir, '.claude/skills', overwrite);
181
+ writeSkills(targetDir, '.claude/skills', overwrite, mode);
142
182
 
143
183
  // Agents as skills (Claude Code skills pattern)
144
- writeAgentsAsSkills(targetDir, '.claude/skills', overwrite);
184
+ writeAgentsAsSkills(targetDir, '.claude/skills', overwrite, mode);
145
185
 
146
186
  // State files
147
- writeStateFiles(targetDir, overwrite);
187
+ writeStateFiles(targetDir, overwrite, mode);
148
188
  }
149
189
 
150
- function generateCursor(targetDir, overwrite) {
190
+ function generateCursor(targetDir, overwrite, mode = 'solo') {
151
191
  // .cursor/rules/core.mdc — dispatcher only (always active)
152
- const coreRules = readTemplate('core-rules.md');
192
+ const coreRules = resolveContent(readTemplate('core-rules.md'), mode);
153
193
  const coreMdc =
154
194
  '---\ndescription: K-Harness dispatcher — workflow guidance and state file references\nalwaysApply: true\n---\n\n' +
155
195
  coreRules;
156
196
  writeFile(targetDir, '.cursor/rules/core.mdc', coreMdc, overwrite);
157
197
 
158
198
  // Skills (.cursor/skills — invokable by mentioning skill name)
159
- writeSkills(targetDir, '.cursor/skills', overwrite);
199
+ writeSkills(targetDir, '.cursor/skills', overwrite, mode);
160
200
 
161
201
  // Agents as skills
162
- writeAgentsAsSkills(targetDir, '.cursor/skills', overwrite);
202
+ writeAgentsAsSkills(targetDir, '.cursor/skills', overwrite, mode);
163
203
 
164
204
  // State files
165
- writeStateFiles(targetDir, overwrite);
205
+ writeStateFiles(targetDir, overwrite, mode);
166
206
  }
167
207
 
168
- function generateCodex(targetDir, overwrite) {
208
+ function generateCodex(targetDir, overwrite, mode = 'solo') {
169
209
  // AGENTS.md — dispatcher only
170
- writeFile(targetDir, 'AGENTS.md', readTemplate('core-rules.md'), overwrite);
210
+ writeFile(targetDir, 'AGENTS.md', resolveContent(readTemplate('core-rules.md'), mode), overwrite);
171
211
 
172
- // Skills (SKILL.md with frontmatter for slash commands)
173
- writeSkills(targetDir, '.agents/skills', overwrite);
212
+ // Skills (SKILL.md with frontmatter invokable via $skill-name)
213
+ writeSkills(targetDir, '.agents/skills', overwrite, mode);
174
214
 
175
- // State files
176
- writeStateFiles(targetDir, overwrite);
177
- }
178
-
179
- function generateWindsurf(targetDir, overwrite) {
180
- // .windsurfrules — dispatcher only (rules are embedded in skills)
181
- writeFile(targetDir, '.windsurfrules', readTemplate('core-rules.md'), overwrite);
215
+ // Agents as skills
216
+ writeAgentsAsSkills(targetDir, '.agents/skills', overwrite, mode);
182
217
 
183
218
  // State files
184
- writeStateFiles(targetDir, overwrite);
219
+ writeStateFiles(targetDir, overwrite, mode);
185
220
  }
186
221
 
187
- function generateAugment(targetDir, overwrite) {
188
- // .augment/rules/core.md — dispatcher only
189
- const coreRules = readTemplate('core-rules.md');
222
+ function generateWindsurf(targetDir, overwrite, mode = 'solo') {
223
+ // .windsurf/rules/core.md — dispatcher (trigger: always_on)
224
+ const coreRules = resolveContent(readTemplate('core-rules.md'), mode);
190
225
  const coreRule =
191
- '---\ndescription: K-Harness dispatcher — workflow guidance and state file references\ntype: always\n---\n\n' +
226
+ '---\ntrigger: always_on\n---\n\n' +
192
227
  coreRules;
193
- writeFile(targetDir, '.augment/rules/core.md', coreRule, overwrite);
228
+ writeFile(targetDir, '.windsurf/rules/core.md', coreRule, overwrite);
194
229
 
195
- // .augment/skills/SKILL.md format (enables / slash commands)
196
- writeSkills(targetDir, '.augment/skills', overwrite);
197
- writeAgentsAsSkills(targetDir, '.augment/skills', overwrite);
230
+ // Skills (.windsurf/skills — Agent Skills standard)
231
+ writeSkills(targetDir, '.windsurf/skills', overwrite, mode);
232
+
233
+ // Agents as skills
234
+ writeAgentsAsSkills(targetDir, '.windsurf/skills', overwrite, mode);
198
235
 
199
236
  // State files
200
- writeStateFiles(targetDir, overwrite);
237
+ writeStateFiles(targetDir, overwrite, mode);
201
238
  }
202
239
 
203
- function generateAntigravity(targetDir, overwrite) {
240
+ function generateAntigravity(targetDir, overwrite, mode = 'solo') {
204
241
  // .agent/rules/core.md — dispatcher only
205
- const coreRules = readTemplate('core-rules.md');
242
+ const coreRules = resolveContent(readTemplate('core-rules.md'), mode);
206
243
  const coreRule =
207
244
  '---\ndescription: K-Harness dispatcher — workflow guidance and state file references\ntype: always\n---\n\n' +
208
245
  coreRules;
209
246
  writeFile(targetDir, '.agent/rules/core.md', coreRule, overwrite);
210
247
 
211
248
  // .agent/skills/ — SKILL.md format (enables / slash commands)
212
- writeSkills(targetDir, '.agent/skills', overwrite);
213
- writeAgentsAsSkills(targetDir, '.agent/skills', overwrite);
249
+ writeSkills(targetDir, '.agent/skills', overwrite, mode);
250
+ writeAgentsAsSkills(targetDir, '.agent/skills', overwrite, mode);
214
251
 
215
252
  // State files
216
- writeStateFiles(targetDir, overwrite);
253
+ writeStateFiles(targetDir, overwrite, mode);
217
254
  }
218
255
 
219
256
  // ─── IDE registry ────────────────────────────────────────────
@@ -223,7 +260,6 @@ const GENERATORS = {
223
260
  cursor: { name: 'Cursor', fn: generateCursor },
224
261
  codex: { name: 'Codex (OpenAI)', fn: generateCodex },
225
262
  windsurf: { name: 'Windsurf', fn: generateWindsurf },
226
- augment: { name: 'Augment Code', fn: generateAugment },
227
263
  antigravity: { name: 'Google Antigravity', fn: generateAntigravity },
228
264
  };
229
265
 
@@ -255,6 +291,85 @@ async function promptIde() {
255
291
  return keys[idx];
256
292
  }
257
293
 
294
+ async function promptMode() {
295
+ console.log(' Project mode:\n');
296
+ console.log(' 1. Solo — Single developer (all state files in docs/)');
297
+ console.log(' 2. Team — Multiple developers (personal state in .harness/, shared in docs/)');
298
+ console.log();
299
+
300
+ const answer = await askQuestion(' Choice (1-2, default: 1): ');
301
+ if (answer === '2' || answer.toLowerCase() === 'team') return 'team';
302
+ return 'solo';
303
+ }
304
+
305
+ // ─── Team mode helpers ───────────────────────────────────────
306
+ function appendGitignore(targetDir) {
307
+ const gitignorePath = path.join(targetDir, '.gitignore');
308
+ const entry = '\n# K-Harness personal state (Team mode)\n.harness/\n';
309
+ if (fs.existsSync(gitignorePath)) {
310
+ const content = fs.readFileSync(gitignorePath, 'utf8');
311
+ if (content.includes('.harness/')) {
312
+ console.log(' ⏭ Skipped (exists): .gitignore entry');
313
+ return;
314
+ }
315
+ fs.appendFileSync(gitignorePath, entry);
316
+ } else {
317
+ fs.writeFileSync(gitignorePath, entry.trimStart());
318
+ }
319
+ console.log(' ✓ .gitignore — added .harness/');
320
+ }
321
+
322
+ function writeGitattributes(targetDir) {
323
+ const content =
324
+ '# K-Harness Team mode — merge strategy for shared state files\n' +
325
+ 'docs/features.md merge=union\n' +
326
+ 'docs/dependency-map.md merge=union\n';
327
+ writeFile(targetDir, '.gitattributes', content, false);
328
+ }
329
+
330
+ // ─── Post-install guide ──────────────────────────────────────
331
+ function showPostInstallGuide(ideName, mode) {
332
+ const modeLabel = mode === 'team' ? 'Team' : 'Solo';
333
+ const lines = [
334
+ '',
335
+ ' ──────────────────────────────────────────',
336
+ ' ✅ K-Harness initialized successfully!',
337
+ '',
338
+ ` Mode: ${modeLabel}`,
339
+ ` IDE: ${ideName}`,
340
+ '',
341
+ ];
342
+
343
+ if (mode === 'team') {
344
+ lines.push(
345
+ ' 📁 Files:',
346
+ ' docs/ — shared state (git committed)',
347
+ ' .harness/ — personal state (gitignored)',
348
+ ' .gitignore — .harness/ added',
349
+ ' .gitattributes — merge=union for shared files',
350
+ );
351
+ } else {
352
+ lines.push(
353
+ ' 📁 Files:',
354
+ ' docs/ — all state files',
355
+ );
356
+ }
357
+
358
+ lines.push(
359
+ '',
360
+ ' 🚀 Next steps:',
361
+ ' 1. Ask your AI: "Run bootstrap to onboard this project"',
362
+ ' 2. AI scans your codebase and fills state files automatically',
363
+ ' 3. Start coding with: @planner "Add [feature name]"',
364
+ '',
365
+ ' 📖 Docs: https://www.npmjs.com/package/k-harness',
366
+ ' ──────────────────────────────────────────',
367
+ '',
368
+ );
369
+
370
+ console.log(lines.join('\n'));
371
+ }
372
+
258
373
  // ─── CLI entry ───────────────────────────────────────────────
259
374
  function showHelp() {
260
375
  console.log(`
@@ -264,7 +379,8 @@ function showHelp() {
264
379
  npx k-harness init [options]
265
380
 
266
381
  Options:
267
- --ide <name> IDE target: vscode, claude, cursor, codex, windsurf, augment, antigravity
382
+ --ide <name> IDE target: vscode, claude, cursor, codex, windsurf, antigravity
383
+ --mode <mode> Project mode: solo (default) or team
268
384
  --dir <path> Target directory (default: current directory)
269
385
  --overwrite Overwrite existing files
270
386
  --help Show this help
@@ -272,16 +388,19 @@ function showHelp() {
272
388
  Examples:
273
389
  npx k-harness init
274
390
  npx k-harness init --ide vscode
391
+ npx k-harness init --ide vscode --mode team
275
392
  npx k-harness init --ide claude --dir ./my-project
276
393
  `);
277
394
  }
278
395
 
279
396
  function parseArgs(argv) {
280
- const args = { command: null, ide: null, dir: process.cwd(), overwrite: false, help: false };
397
+ const args = { command: null, ide: null, mode: null, dir: process.cwd(), overwrite: false, help: false };
281
398
  for (let i = 0; i < argv.length; i++) {
282
399
  const arg = argv[i];
283
400
  if (arg === 'init') args.command = 'init';
284
401
  else if (arg === '--ide' && argv[i + 1]) { args.ide = argv[++i]; }
402
+ else if (arg === '--mode' && argv[i + 1]) { args.mode = argv[++i]; }
403
+ else if (arg === '--team') { args.mode = 'team'; }
285
404
  else if (arg === '--dir' && argv[i + 1]) { args.dir = path.resolve(argv[++i]); }
286
405
  else if (arg === '--overwrite') args.overwrite = true;
287
406
  else if (arg === '--help' || arg === '-h') args.help = true;
@@ -311,11 +430,29 @@ async function run(argv) {
311
430
  ide = await promptIde();
312
431
  }
313
432
 
433
+ // Determine mode
434
+ let mode = args.mode;
435
+ if (mode && !['solo', 'team'].includes(mode)) {
436
+ console.error(` Unknown mode: ${mode}`);
437
+ console.error(' Available: solo, team');
438
+ process.exit(1);
439
+ }
440
+ if (!mode) {
441
+ mode = await promptMode();
442
+ }
443
+
314
444
  const gen = GENERATORS[ide];
315
445
  const lang = detectLanguage(args.dir);
316
- console.log(`\n Installing for ${gen.name}... (detected language: ${lang})\n`);
317
- gen.fn(args.dir, args.overwrite);
318
- console.log(`\n Done! Run "bootstrap" in your AI chat to auto-fill state files and rules.\n`);
446
+ console.log(`\n Installing for ${gen.name} (${mode} mode)... (detected language: ${lang})\n`);
447
+ gen.fn(args.dir, args.overwrite, mode);
448
+
449
+ // Team mode extras
450
+ if (mode === 'team') {
451
+ appendGitignore(args.dir);
452
+ writeGitattributes(args.dir);
453
+ }
454
+
455
+ showPostInstallGuide(gen.name, mode);
319
456
  }
320
457
  }
321
458