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 +4 -6
- package/harness/dependency-map.md +1 -0
- package/harness/features.md +1 -0
- package/harness/skills/pivot.md +13 -0
- package/package.json +1 -1
- package/src/init.js +191 -54
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`, `
|
|
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` |
|
|
62
|
-
| **Windsurf** | `.
|
|
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) |
|
|
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
|
|
package/harness/features.md
CHANGED
package/harness/skills/pivot.md
CHANGED
|
@@ -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
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
176
|
-
|
|
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
|
|
188
|
-
// .
|
|
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
|
-
'---\
|
|
226
|
+
'---\ntrigger: always_on\n---\n\n' +
|
|
192
227
|
coreRules;
|
|
193
|
-
writeFile(targetDir, '.
|
|
228
|
+
writeFile(targetDir, '.windsurf/rules/core.md', coreRule, overwrite);
|
|
194
229
|
|
|
195
|
-
// .
|
|
196
|
-
writeSkills(targetDir, '.
|
|
197
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|