feat: add SpecDialog component for editing spec frontmatter

- Implement SpecDialog for managing spec details including title, read mode, priority, and keywords.
- Add validation and keyword management functionality.
- Integrate SpecDialog into SpecsSettingsPage for editing specs.

feat: create index file for specs components

- Export SpecCard, SpecDialog, and related types from a new index file for better organization.

feat: implement SpecsSettingsPage for managing specs and hooks

- Create main settings page with tabs for Project Specs, Personal Specs, Hooks, Injection, and Settings.
- Integrate SpecDialog and HookDialog for editing specs and hooks.
- Add search functionality and mock data for specs and hooks.

feat: add spec management API routes

- Implement API endpoints for listing specs, getting spec details, updating frontmatter, rebuilding indices, and initializing the spec system.
- Handle errors and responses appropriately for each endpoint.
This commit is contained in:
catlog22
2026-02-26 22:03:13 +08:00
parent 430d817e43
commit 6155fcc7b8
115 changed files with 4883 additions and 21127 deletions

View File

@@ -111,7 +111,7 @@ rm -f .workflow/archives/$SESSION_ID/.archiving
### Phase 4: Auto-Sync Project State
Execute `/workflow:session:sync -y "{description}"` to update both `project-guidelines.json` and `project-tech.json` from session context.
Execute `/workflow:session:sync -y "{description}"` to update both `specs/*.md` and `project-tech.json` from session context.
Description 取自 Phase 2 的 `workflow-session.json` description 字段。
@@ -135,5 +135,5 @@ When `--yes` or `-y` flag is used:
Phase 1: find session → create .archiving marker
Phase 2: read key files → build manifest entry (no writes)
Phase 3: mkdir → mv → update manifest.json → rm marker
Phase 4: /workflow:session:sync -y → update project-guidelines + project-tech
Phase 4: /workflow:session:sync -y → update specs/*.md + project-tech
```

View File

@@ -18,7 +18,7 @@ When `--yes` or `-y`: Auto-categorize and add guideline without confirmation.
## Overview
Crystallizes ephemeral session context (insights, decisions, constraints) into permanent project guidelines stored in `.workflow/project-guidelines.json`. This ensures valuable learnings persist across sessions and inform future planning.
Crystallizes ephemeral session context (insights, decisions, constraints) into permanent project guidelines stored in `.workflow/specs/*.md`. This ensures valuable learnings persist across sessions and inform future planning.
## Use Cases
@@ -113,34 +113,14 @@ ELSE (convention/constraint/learning):
### Step 1: Ensure Guidelines File Exists
```bash
bash(test -f .workflow/project-guidelines.json && echo "EXISTS" || echo "NOT_FOUND")
bash(test -f .workflow/specs/coding-conventions.md && echo "EXISTS" || echo "NOT_FOUND")
```
**If NOT_FOUND**, create scaffold:
**If NOT_FOUND**, initialize spec system:
```javascript
const scaffold = {
conventions: {
coding_style: [],
naming_patterns: [],
file_structure: [],
documentation: []
},
constraints: {
architecture: [],
tech_stack: [],
performance: [],
security: []
},
quality_rules: [],
learnings: [],
_metadata: {
created_at: new Date().toISOString(),
version: "1.0.0"
}
};
Write('.workflow/project-guidelines.json', JSON.stringify(scaffold, null, 2));
```bash
Bash('ccw spec init')
Bash('ccw spec rebuild')
```
### Step 2: Auto-detect Type (if not specified)
@@ -203,33 +183,40 @@ function buildEntry(rule, type, category, sessionId) {
}
```
### Step 4: Update Guidelines File
### Step 4: Update Spec Files
```javascript
const guidelines = JSON.parse(Read('.workflow/project-guidelines.json'));
if (type === 'convention') {
if (!guidelines.conventions[category]) {
guidelines.conventions[category] = [];
}
if (!guidelines.conventions[category].includes(rule)) {
guidelines.conventions[category].push(rule);
}
} else if (type === 'constraint') {
if (!guidelines.constraints[category]) {
guidelines.constraints[category] = [];
}
if (!guidelines.constraints[category].includes(rule)) {
guidelines.constraints[category].push(rule);
}
} else if (type === 'learning') {
guidelines.learnings.push(buildEntry(rule, type, category, sessionId));
// Map type+category to target spec file
const specFileMap = {
convention: '.workflow/specs/coding-conventions.md',
constraint: '.workflow/specs/architecture-constraints.md'
}
guidelines._metadata.updated_at = new Date().toISOString();
guidelines._metadata.last_solidified_by = sessionId;
if (type === 'convention' || type === 'constraint') {
const targetFile = specFileMap[type]
const existing = Read(targetFile)
Write('.workflow/project-guidelines.json', JSON.stringify(guidelines, null, 2));
// Deduplicate: skip if rule text already exists in the file
if (!existing.includes(rule)) {
const ruleText = `- [${category}] ${rule}`
const newContent = existing.trimEnd() + '\n' + ruleText + '\n'
Write(targetFile, newContent)
}
} else if (type === 'learning') {
// Learnings go to coding-conventions.md as a special section
const targetFile = '.workflow/specs/coding-conventions.md'
const existing = Read(targetFile)
const entry = buildEntry(rule, type, category, sessionId)
const learningText = `- [learning/${category}] ${entry.insight} (${entry.date})`
if (!existing.includes(entry.insight)) {
const newContent = existing.trimEnd() + '\n' + learningText + '\n'
Write(targetFile, newContent)
}
}
// Rebuild spec index after modification
Bash('ccw spec rebuild')
```
### Step 5: Display Confirmation
@@ -241,7 +228,7 @@ Type: ${type}
Category: ${category}
Rule: "${rule}"
Location: .workflow/project-guidelines.json -> ${type}s.${category}
Location: .workflow/specs/*.md -> ${type}s.${category}
Total ${type}s in ${category}: ${count}
```
@@ -386,7 +373,7 @@ AskUserQuestion({
/workflow:session:solidify "Use async/await instead of callbacks" --type convention --category coding_style
```
Result in `project-guidelines.json`:
Result in `specs/*.md`:
```json
{
"conventions": {
@@ -444,7 +431,7 @@ Result: Creates a new CMEM with consolidated content from the 10 most recent non
## Integration with Planning
The `project-guidelines.json` is consumed by:
The `specs/*.md` is consumed by:
1. **`workflow-plan` skill (context-gather phase)**: Loads guidelines into context-package.json
2. **`workflow-plan` skill**: Passes guidelines to task generation agent
@@ -462,4 +449,4 @@ This ensures all future planning respects solidified rules without users needing
- `/workflow:session:start` - Start a session (may prompt for solidify at end)
- `/workflow:session:complete` - Complete session (prompts for learnings to solidify)
- `/workflow:init` - Creates project-guidelines.json scaffold if missing
- `/workflow:init` - Creates specs/*.md scaffold if missing

View File

@@ -44,7 +44,7 @@ ERROR: Invalid session type. Valid types: workflow, review, tdd, test, docs
```bash
# Check if project state exists (both files required)
bash(test -f .workflow/project-tech.json && echo "TECH_EXISTS" || echo "TECH_NOT_FOUND")
bash(test -f .workflow/project-guidelines.json && echo "GUIDELINES_EXISTS" || echo "GUIDELINES_NOT_FOUND")
bash(test -f .workflow/specs/*.md && echo "GUIDELINES_EXISTS" || echo "GUIDELINES_NOT_FOUND")
```
**If either NOT_FOUND**, delegate to `/workflow:init`:
@@ -53,14 +53,14 @@ bash(test -f .workflow/project-guidelines.json && echo "GUIDELINES_EXISTS" || ec
Skill(skill="workflow:init");
// Wait for init completion
// project-tech.json and project-guidelines.json will be created
// project-tech.json and specs/*.md will be created
```
**Output**:
- If BOTH_EXIST: `PROJECT_STATE: initialized`
- If NOT_FOUND: Calls `/workflow:init` → creates:
- `.workflow/project-tech.json` with full technical analysis
- `.workflow/project-guidelines.json` with empty scaffold
- `.workflow/specs/*.md` with empty scaffold
**Note**: `/workflow:init` uses cli-explore-agent to build comprehensive project understanding (technology stack, architecture, key components). This step runs once per project. Subsequent executions skip initialization.

View File

@@ -1,13 +1,13 @@
---
name: sync
description: Quick-sync session work to project-guidelines and project-tech
description: Quick-sync session work to specs/*.md and project-tech
argument-hint: "[-y|--yes] [\"what was done\"]"
allowed-tools: Bash(*), Read(*), Write(*), Edit(*)
---
# Session Sync (/workflow:session:sync)
One-shot update `project-guidelines.json` + `project-tech.json` from current session context.
One-shot update `specs/*.md` + `project-tech.json` from current session context.
**Design**: Scan context → extract → write. No interactive wizards.
@@ -73,7 +73,8 @@ Analyze context and produce two update payloads. Use LLM reasoning (current agen
// RULE: Only extract genuinely reusable insights. Skip trivial/obvious items.
// RULE: Deduplicate against existing guidelines before adding.
const existingGuidelines = JSON.parse(Read('.workflow/project-guidelines.json'))
// Load existing specs via ccw spec load
const existingSpecs = Bash('ccw spec load --dimension specs 2>/dev/null || echo ""')
const guidelineUpdates = [] // populated by agent analysis
// ── Tech extraction ──
@@ -123,7 +124,7 @@ Tech [${detectCategory(summary)}]:
${techEntry.title}
Target files:
.workflow/project-guidelines.json
.workflow/specs/*.md
.workflow/project-tech.json
`)
@@ -136,33 +137,31 @@ if (!autoYes) {
## Step 4: Write
```javascript
// ── Update project-guidelines.json ──
// ── Update specs/*.md ──
if (guidelineUpdates.length > 0) {
const guidelines = JSON.parse(Read('.workflow/project-guidelines.json'))
// Map guideline types to spec files
const specFileMap = {
convention: '.workflow/specs/coding-conventions.md',
constraint: '.workflow/specs/architecture-constraints.md',
learning: '.workflow/specs/coding-conventions.md' // learnings appended to conventions
}
for (const g of guidelineUpdates) {
if (g.type === 'learning') {
// Deduplicate by insight text
if (!guidelines.learnings.some(l => l.insight === g.text)) {
guidelines.learnings.push({
date: new Date().toISOString().split('T')[0],
session_id: techEntry.session_id,
insight: g.text,
category: g.category
})
}
} else {
// convention or constraint
const section = g.type === 'convention' ? 'conventions' : 'constraints'
if (!guidelines[section][g.category]) guidelines[section][g.category] = []
if (!guidelines[section][g.category].includes(g.text)) {
guidelines[section][g.category].push(g.text)
}
const targetFile = specFileMap[g.type]
const existing = Read(targetFile)
const ruleText = g.type === 'learning'
? `- [${g.category}] ${g.text} (learned: ${new Date().toISOString().split('T')[0]})`
: `- [${g.category}] ${g.text}`
// Deduplicate: skip if text already in file
if (!existing.includes(g.text)) {
const newContent = existing.trimEnd() + '\n' + ruleText + '\n'
Write(targetFile, newContent)
}
}
guidelines._metadata.updated_at = new Date().toISOString()
Write('.workflow/project-guidelines.json', JSON.stringify(guidelines, null, 2))
// Rebuild spec index after writing
Bash('ccw spec rebuild')
}
// ── Update project-tech.json ──