Merge pull request #1154 from gsd-build/Solvely/soft-gsd-workflow-enforcement
feat(new-project): add soft GSD workflow enforcement
This commit is contained in:
@@ -23,7 +23,7 @@ Initialize a new project with deep context gathering.
|
||||
| `--auto @file.md` | Auto-extract from document, skip interactive questions |
|
||||
|
||||
**Prerequisites:** No existing `.planning/PROJECT.md`
|
||||
**Produces:** `PROJECT.md`, `REQUIREMENTS.md`, `ROADMAP.md`, `STATE.md`, `config.json`, `research/`
|
||||
**Produces:** `PROJECT.md`, `REQUIREMENTS.md`, `ROADMAP.md`, `STATE.md`, `config.json`, `research/`, `CLAUDE.md`
|
||||
|
||||
```bash
|
||||
/gsd:new-project # Interactive mode
|
||||
|
||||
@@ -179,6 +179,17 @@ const CLAUDE_MD_FALLBACKS = {
|
||||
architecture: 'Architecture not yet mapped. Follow existing patterns found in the codebase.',
|
||||
};
|
||||
|
||||
const CLAUDE_MD_WORKFLOW_ENFORCEMENT = [
|
||||
'Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.',
|
||||
'',
|
||||
'Use these entry points:',
|
||||
'- `/gsd:quick` for small fixes, doc updates, and ad-hoc tasks',
|
||||
'- `/gsd:debug` for investigation and bug fixing',
|
||||
'- `/gsd:execute-phase` for planned phase work',
|
||||
'',
|
||||
'Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it.',
|
||||
].join('\n');
|
||||
|
||||
const CLAUDE_MD_PROFILE_PLACEHOLDER = [
|
||||
'<!-- GSD:profile-start -->',
|
||||
'## Developer Profile',
|
||||
@@ -356,6 +367,14 @@ function generateArchitectureSection(cwd) {
|
||||
return { content: summary, source: 'ARCHITECTURE.md', hasFallback: false };
|
||||
}
|
||||
|
||||
function generateWorkflowSection() {
|
||||
return {
|
||||
content: CLAUDE_MD_WORKFLOW_ENFORCEMENT,
|
||||
source: 'GSD defaults',
|
||||
hasFallback: false,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Commands ─────────────────────────────────────────────────────────────────
|
||||
|
||||
function cmdWriteProfile(cwd, options, raw) {
|
||||
@@ -796,18 +815,20 @@ function cmdGenerateClaudeProfile(cwd, options, raw) {
|
||||
}
|
||||
|
||||
function cmdGenerateClaudeMd(cwd, options, raw) {
|
||||
const MANAGED_SECTIONS = ['project', 'stack', 'conventions', 'architecture'];
|
||||
const MANAGED_SECTIONS = ['project', 'stack', 'conventions', 'architecture', 'workflow'];
|
||||
const generators = {
|
||||
project: generateProjectSection,
|
||||
stack: generateStackSection,
|
||||
conventions: generateConventionsSection,
|
||||
architecture: generateArchitectureSection,
|
||||
workflow: generateWorkflowSection,
|
||||
};
|
||||
const sectionHeadings = {
|
||||
project: '## Project',
|
||||
stack: '## Technology Stack',
|
||||
conventions: '## Conventions',
|
||||
architecture: '## Architecture',
|
||||
workflow: '## GSD Workflow Enforcement',
|
||||
};
|
||||
|
||||
const generated = {};
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
Template for project-root `CLAUDE.md` — auto-generated by `gsd-tools generate-claude-md`.
|
||||
|
||||
Contains 5 marker-bounded sections. Each section is independently updatable.
|
||||
The `generate-claude-md` subcommand manages 4 sections (project, stack, conventions, architecture).
|
||||
Contains 6 marker-bounded sections. Each section is independently updatable.
|
||||
The `generate-claude-md` subcommand manages 5 sections (project, stack, conventions, architecture, workflow enforcement).
|
||||
The profile section is managed exclusively by `generate-claude-profile`.
|
||||
|
||||
---
|
||||
@@ -66,6 +66,22 @@ Conventions not yet established. Will populate as patterns emerge during develop
|
||||
Architecture not yet mapped. Follow existing patterns found in the codebase.
|
||||
```
|
||||
|
||||
### Workflow Enforcement Section
|
||||
```
|
||||
<!-- GSD:workflow-start source:GSD defaults -->
|
||||
## GSD Workflow Enforcement
|
||||
|
||||
Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.
|
||||
|
||||
Use these entry points:
|
||||
- `/gsd:quick` for small fixes, doc updates, and ad-hoc tasks
|
||||
- `/gsd:debug` for investigation and bug fixing
|
||||
- `/gsd:execute-phase` for planned phase work
|
||||
|
||||
Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it.
|
||||
<!-- GSD:workflow-end -->
|
||||
```
|
||||
|
||||
### Profile Section (Placeholder Only)
|
||||
```
|
||||
<!-- GSD:profile-start -->
|
||||
@@ -88,7 +104,8 @@ CLAUDE.md file and no profile section exists yet.
|
||||
2. **Stack** — Technology choices (what tools are used)
|
||||
3. **Conventions** — Code patterns and rules (how code is written)
|
||||
4. **Architecture** — System structure (how components fit together)
|
||||
5. **Profile** — Developer behavioral preferences (how to interact)
|
||||
5. **Workflow Enforcement** — Default GSD entry points for file-changing work
|
||||
6. **Profile** — Developer behavioral preferences (how to interact)
|
||||
|
||||
## Marker Format
|
||||
|
||||
|
||||
@@ -1039,10 +1039,18 @@ Use AskUserQuestion:
|
||||
|
||||
**If "Review full file":** Display raw `cat .planning/ROADMAP.md`, then re-ask.
|
||||
|
||||
**Generate or refresh project CLAUDE.md before final commit:**
|
||||
|
||||
```bash
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" generate-claude-md
|
||||
```
|
||||
|
||||
This ensures new projects get the default GSD workflow-enforcement guidance and current project context in `CLAUDE.md`.
|
||||
|
||||
**Commit roadmap (after approval or auto mode):**
|
||||
|
||||
```bash
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs: create roadmap ([N] phases)" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs: create roadmap ([N] phases)" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md CLAUDE.md
|
||||
```
|
||||
|
||||
## 9. Done
|
||||
@@ -1063,6 +1071,7 @@ Present completion summary:
|
||||
| Research | `.planning/research/` |
|
||||
| Requirements | `.planning/REQUIREMENTS.md` |
|
||||
| Roadmap | `.planning/ROADMAP.md` |
|
||||
| Project guide | `CLAUDE.md` |
|
||||
|
||||
**[N] phases** | **[X] requirements** | Ready to build ✓
|
||||
```
|
||||
@@ -1113,6 +1122,7 @@ Exit skill and invoke SlashCommand("/gsd:discuss-phase 1 --auto")
|
||||
- `.planning/REQUIREMENTS.md`
|
||||
- `.planning/ROADMAP.md`
|
||||
- `.planning/STATE.md`
|
||||
- `CLAUDE.md`
|
||||
|
||||
</output>
|
||||
|
||||
@@ -1134,6 +1144,7 @@ Exit skill and invoke SlashCommand("/gsd:discuss-phase 1 --auto")
|
||||
- [ ] ROADMAP.md created with phases, requirement mappings, success criteria
|
||||
- [ ] STATE.md initialized
|
||||
- [ ] REQUIREMENTS.md traceability updated
|
||||
- [ ] CLAUDE.md generated with GSD workflow guidance
|
||||
- [ ] User knows next step is `/gsd:discuss-phase 1`
|
||||
|
||||
**Atomic commits:** Each phase commits its artifacts immediately. If context is lost, artifacts persist.
|
||||
|
||||
82
tests/claude-md.test.cjs
Normal file
82
tests/claude-md.test.cjs
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* CLAUDE.md generation and new-project workflow tests
|
||||
*/
|
||||
|
||||
const { test, describe, beforeEach, afterEach } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
describe('generate-claude-md', () => {
|
||||
let tmpDir;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = createTempProject();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(tmpDir);
|
||||
});
|
||||
|
||||
test('creates CLAUDE.md with workflow enforcement section', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'PROJECT.md'),
|
||||
'# Test Project\n\n## What This Is\n\nA small test project.\n'
|
||||
);
|
||||
|
||||
const result = runGsdTools('generate-claude-md', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.action, 'created');
|
||||
assert.strictEqual(output.sections_total, 5);
|
||||
assert.ok(output.sections_generated.includes('workflow'));
|
||||
|
||||
const claudePath = path.join(tmpDir, 'CLAUDE.md');
|
||||
const content = fs.readFileSync(claudePath, 'utf-8');
|
||||
assert.ok(content.includes('## GSD Workflow Enforcement'));
|
||||
assert.ok(content.includes('/gsd:quick'));
|
||||
assert.ok(content.includes('/gsd:debug'));
|
||||
assert.ok(content.includes('/gsd:execute-phase'));
|
||||
assert.ok(content.includes('Do not make direct repo edits outside a GSD workflow'));
|
||||
});
|
||||
|
||||
test('adds workflow enforcement section when updating an existing CLAUDE.md', () => {
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, '.planning', 'PROJECT.md'),
|
||||
'# Test Project\n\n## What This Is\n\nA small test project.\n'
|
||||
);
|
||||
fs.writeFileSync(path.join(tmpDir, 'CLAUDE.md'), '## Local Notes\n\nKeep this intro.\n');
|
||||
|
||||
const result = runGsdTools('generate-claude-md', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const output = JSON.parse(result.output);
|
||||
assert.strictEqual(output.action, 'updated');
|
||||
|
||||
const content = fs.readFileSync(path.join(tmpDir, 'CLAUDE.md'), 'utf-8');
|
||||
assert.ok(content.includes('## Local Notes'));
|
||||
assert.ok(content.includes('## GSD Workflow Enforcement'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('new-project workflow includes CLAUDE.md generation', () => {
|
||||
const workflowPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'new-project.md');
|
||||
const commandsPath = path.join(__dirname, '..', 'docs', 'COMMANDS.md');
|
||||
|
||||
test('new-project workflow generates CLAUDE.md before final commit', () => {
|
||||
const content = fs.readFileSync(workflowPath, 'utf-8');
|
||||
assert.ok(content.includes('generate-claude-md'));
|
||||
assert.ok(content.includes('--files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md CLAUDE.md'));
|
||||
});
|
||||
|
||||
test('new-project artifacts mention CLAUDE.md', () => {
|
||||
const workflowContent = fs.readFileSync(workflowPath, 'utf-8');
|
||||
const commandsContent = fs.readFileSync(commandsPath, 'utf-8');
|
||||
|
||||
assert.ok(workflowContent.includes('| Project guide | `CLAUDE.md`'));
|
||||
assert.ok(workflowContent.includes('- `CLAUDE.md`'));
|
||||
assert.ok(commandsContent.includes('`CLAUDE.md`'));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user