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

@@ -834,7 +834,7 @@ todos = [
| `workflow:unified-execute-with-file` | Universal execution engine - consumes plan output from collaborative-plan, req-plan, brainstorm |
| `workflow:clean` | Intelligent code cleanup - mainline detection, stale artifact removal |
| `workflow:init` | Initialize `.workflow/project-tech.json` with project analysis |
| `workflow:init-guidelines` | Interactive wizard to fill `project-guidelines.json` |
| `workflow:init-guidelines` | Interactive wizard to fill `specs/*.md` |
---

View File

@@ -160,7 +160,7 @@ ${issueList}
### Project Context (MANDATORY)
1. Read: .workflow/project-tech.json (technology stack, architecture)
2. Read: .workflow/project-guidelines.json (constraints and conventions)
2. Read: .workflow/specs/*.md (constraints and conventions)
### Workflow
1. Fetch issue details: ccw issue status <id> --json

View File

@@ -571,27 +571,22 @@ CONSTRAINTS: ${perspective.constraints}
.map(r => r.action)
.join('\n') || conclusions.summary
// 2. Extract exploration digest (inline data, not path reference)
const explorationDigest = { relevant_files: [], patterns: [], key_findings: [] }
// 2. Assemble compact analysis context as inline memory block
const contextLines = [
`## Prior Analysis (${sessionId})`,
`**Summary**: ${conclusions.summary}`
]
const codebasePath = `${sessionFolder}/exploration-codebase.json`
if (file_exists(codebasePath)) {
const data = JSON.parse(Read(codebasePath))
explorationDigest.relevant_files = data.relevant_files || []
explorationDigest.patterns = data.patterns || []
explorationDigest.key_findings = data.key_findings || []
const files = (data.relevant_files || []).slice(0, 8).map(f => f.path || f.file || f).filter(Boolean)
const findings = (data.key_findings || []).slice(0, 5)
if (files.length) contextLines.push(`**Key Files**: ${files.join(', ')}`)
if (findings.length) contextLines.push(`**Key Findings**:\n${findings.map(f => `- ${f}`).join('\n')}`)
}
// 3. Write handoff file to analysis session folder
Write(`${sessionFolder}/handoff-lite-plan.json`, JSON.stringify({
source_session: sessionId,
summary: conclusions.summary,
recommendations: conclusions.recommendations,
decision_trail: conclusions.decision_trail,
exploration_digest: explorationDigest
}, null, 2))
// 4. Call lite-plan with --from-analysis handoff
Skill(skill="workflow-lite-plan", args=`--from-analysis ${sessionFolder}/handoff-lite-plan.json "${taskDescription}"`)
// 3. Call lite-plan with enriched task description (no special flags)
Skill(skill="workflow-lite-plan", args=`"${taskDescription}\n\n${contextLines.join('\n')}"`)
}
```
@@ -774,7 +769,7 @@ User agrees with current direction, wants deeper code analysis
- Need simple task breakdown
- Focus on quick execution planning
> **Note**: Phase 4「生成任务」auto-generates `--from-analysis` handoff. Manual invocation normally not needed after analysis.
> **Note**: Phase 4「生成任务」assembles analysis context as inline `## Prior Analysis` block in task description, allowing lite-plan to skip redundant exploration automatically.
---

View File

@@ -495,8 +495,8 @@ if (fileExists(projectPath)) {
Write(projectPath, JSON.stringify(project, null, 2))
}
// Update project-guidelines.json: remove learnings referencing deleted sessions
const guidelinesPath = '.workflow/project-guidelines.json'
// Update specs/*.md: remove learnings referencing deleted sessions
const guidelinesPath = '.workflow/specs/*.md'
if (fileExists(guidelinesPath)) {
const guidelines = JSON.parse(Read(guidelinesPath))
const deletedSessionIds = results.deleted
@@ -566,7 +566,7 @@ Cleanup manifest archived to: ${sessionFolder}/cleanup-manifest.json
## Related Commands
- `/workflow:session:sync` - Sync session work to project-guidelines + project-tech (正向写入)
- `/workflow:session:sync` - Sync session work to specs/*.md + project-tech (正向写入)
- `/workflow:session:complete` - Properly archive active sessions
- `memory-capture` skill - Save session memory before cleanup
- `workflow-execute` skill - View current workflow state

View File

@@ -208,7 +208,7 @@ Task(
### Project Context (MANDATORY)
Read and incorporate:
- \`.workflow/project-tech.json\` (if exists): Technology stack, architecture
- \`.workflow/project-guidelines.json\` (if exists): Constraints, conventions -- apply as HARD CONSTRAINTS on sub-domain splitting and plan structure
- \`.workflow/specs/*.md\` (if exists): Constraints, conventions -- apply as HARD CONSTRAINTS on sub-domain splitting and plan structure
### Input Requirements
${taskDescription}
@@ -357,7 +357,7 @@ subDomains.map(sub =>
### Project Context (MANDATORY)
Read and incorporate:
- \`.workflow/project-tech.json\` (if exists): Technology stack, architecture
- \`.workflow/project-guidelines.json\` (if exists): Constraints, conventions -- apply as HARD CONSTRAINTS
- \`.workflow/specs/*.md\` (if exists): Constraints, conventions -- apply as HARD CONSTRAINTS
## Dual Output Tasks

View File

@@ -630,7 +630,7 @@ Why is config value None during update?
## Post-Completion Expansion
**Auto-sync**: 执行 `/workflow:session:sync -y "{summary}"` 更新 project-guidelines + project-tech。
**Auto-sync**: 执行 `/workflow:session:sync -y "{summary}"` 更新 specs/*.md + project-tech。
完成后询问用户是否扩展为issue(test/enhance/refactor/doc),选中项调用 `/issue:new "{summary} - {dimension}"`

View File

@@ -1,6 +1,6 @@
---
name: init-guidelines
description: Interactive wizard to fill project-guidelines.json based on project analysis
description: Interactive wizard to fill specs/*.md based on project analysis
argument-hint: "[--reset]"
examples:
- /workflow:init-guidelines
@@ -11,7 +11,7 @@ examples:
## Overview
Interactive multi-round wizard that analyzes the current project (via `project-tech.json`) and asks targeted questions to populate `.workflow/project-guidelines.json` with coding conventions, constraints, and quality rules.
Interactive multi-round wizard that analyzes the current project (via `project-tech.json`) and asks targeted questions to populate `.workflow/specs/*.md` with coding conventions, constraints, and quality rules.
**Design Principle**: Questions are dynamically generated based on the project's tech stack, architecture, and patterns — not generic boilerplate.
@@ -31,7 +31,7 @@ Input Parsing:
Step 1: Check Prerequisites
├─ project-tech.json must exist (run /workflow:init first)
├─ project-guidelines.json: check if populated or scaffold-only
├─ specs/*.md: check if populated or scaffold-only
└─ If populated + no --reset → Ask: "Guidelines already exist. Overwrite or append?"
Step 2: Load Project Context
@@ -44,7 +44,7 @@ Step 3: Multi-Round Interactive Questionnaire
├─ Round 4: Performance & Security Constraints (performance, security)
└─ Round 5: Quality Rules (quality_rules)
Step 4: Write project-guidelines.json
Step 4: Write specs/*.md
Step 5: Display Summary
```
@@ -55,7 +55,7 @@ Step 5: Display Summary
```bash
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/coding-conventions.md && echo "SPECS_EXISTS" || echo "SPECS_NOT_FOUND")
```
**If TECH_NOT_FOUND**: Exit with message
@@ -71,12 +71,10 @@ const reset = $ARGUMENTS.includes('--reset')
**If GUIDELINES_EXISTS and not --reset**: Check if guidelines are populated (not just scaffold)
```javascript
const guidelines = JSON.parse(Read('.workflow/project-guidelines.json'))
const isPopulated =
guidelines.conventions.coding_style.length > 0 ||
guidelines.conventions.naming_patterns.length > 0 ||
guidelines.constraints.architecture.length > 0 ||
guidelines.constraints.tech_stack.length > 0
// Check if specs already have content via ccw spec list
const specsList = Bash('ccw spec list --json 2>/dev/null || echo "{}"')
const specsData = JSON.parse(specsList)
const isPopulated = (specsData.total || 0) > 5 // More than seed docs
if (isPopulated) {
AskUserQuestion({
@@ -326,64 +324,76 @@ AskUserQuestion({
**Process Round 5 answers** → add to `quality_rules` array as `{ rule, scope, enforced_by }` objects.
### Step 4: Write project-guidelines.json
### Step 4: Write specs/*.md
For each category of collected answers, append rules to the corresponding spec MD file. Each spec file uses YAML frontmatter with `readMode`, `priority`, and `keywords`.
```javascript
// Build the final guidelines object
const finalGuidelines = {
conventions: {
coding_style: existingCodingStyle.concat(newCodingStyle),
naming_patterns: existingNamingPatterns.concat(newNamingPatterns),
file_structure: existingFileStructure.concat(newFileStructure),
documentation: existingDocumentation.concat(newDocumentation)
},
constraints: {
architecture: existingArchitecture.concat(newArchitecture),
tech_stack: existingTechStack.concat(newTechStack),
performance: existingPerformance.concat(newPerformance),
security: existingSecurity.concat(newSecurity)
},
quality_rules: existingQualityRules.concat(newQualityRules),
learnings: existingLearnings, // Preserve existing learnings
_metadata: {
created_at: existingMetadata?.created_at || new Date().toISOString(),
version: "1.0.0",
last_updated: new Date().toISOString(),
updated_by: "workflow:init-guidelines"
}
// Helper: append rules to a spec MD file
function appendRulesToSpecFile(filePath, rules) {
if (rules.length === 0) return
const existing = Read(filePath)
// Append new rules as markdown list items after existing content
const newContent = existing.trimEnd() + '\n' + rules.map(r => `- ${r}`).join('\n') + '\n'
Write(filePath, newContent)
}
Write('.workflow/project-guidelines.json', JSON.stringify(finalGuidelines, null, 2))
// Write conventions
appendRulesToSpecFile('.workflow/specs/coding-conventions.md',
[...newCodingStyle, ...newNamingPatterns, ...newFileStructure, ...newDocumentation])
// Write constraints
appendRulesToSpecFile('.workflow/specs/architecture-constraints.md',
[...newArchitecture, ...newTechStack, ...newPerformance, ...newSecurity])
// Write quality rules (create file if needed)
if (newQualityRules.length > 0) {
const qualityPath = '.workflow/specs/quality-rules.md'
if (!file_exists(qualityPath)) {
Write(qualityPath, `---
title: Quality Rules
readMode: required
priority: high
keywords: [quality, testing, coverage, lint]
---
# Quality Rules
`)
}
appendRulesToSpecFile(qualityPath,
newQualityRules.map(q => `${q.rule} (scope: ${q.scope}, enforced by: ${q.enforced_by})`))
}
// Rebuild spec index after writing
Bash('ccw spec rebuild')
```
### Step 5: Display Summary
```javascript
const countConventions = finalGuidelines.conventions.coding_style.length
+ finalGuidelines.conventions.naming_patterns.length
+ finalGuidelines.conventions.file_structure.length
+ finalGuidelines.conventions.documentation.length
const countConventions = newCodingStyle.length + newNamingPatterns.length
+ newFileStructure.length + newDocumentation.length
const countConstraints = newArchitecture.length + newTechStack.length
+ newPerformance.length + newSecurity.length
const countQuality = newQualityRules.length
const countConstraints = finalGuidelines.constraints.architecture.length
+ finalGuidelines.constraints.tech_stack.length
+ finalGuidelines.constraints.performance.length
+ finalGuidelines.constraints.security.length
const countQuality = finalGuidelines.quality_rules.length
// Get updated spec list
const specsList = Bash('ccw spec list --json 2>/dev/null || echo "{}"')
console.log(`
✓ Project guidelines configured
## Summary
- Conventions: ${countConventions} rules (coding: ${cs}, naming: ${np}, files: ${fs}, docs: ${doc})
- Constraints: ${countConstraints} rules (arch: ${ar}, tech: ${ts}, perf: ${pf}, security: ${sc})
- Quality rules: ${countQuality}
- Conventions: ${countConventions} rules added to coding-conventions.md
- Constraints: ${countConstraints} rules added to architecture-constraints.md
- Quality rules: ${countQuality} rules added to quality-rules.md
File: .workflow/project-guidelines.json
Spec index rebuilt. Use \`ccw spec list\` to view all specs.
Next steps:
- Use /workflow:session:solidify to add individual rules later
- Guidelines will be auto-loaded by /workflow:plan for task generation
- Specs are auto-loaded via hook on each prompt
`)
```

View File

@@ -10,11 +10,11 @@ examples:
# Workflow Init Command (/workflow:init)
## Overview
Initialize `.workflow/project-tech.json` and `.workflow/project-guidelines.json` with comprehensive project understanding by delegating analysis to **cli-explore-agent**.
Initialize `.workflow/project-tech.json` and `.workflow/specs/*.md` with comprehensive project understanding by delegating analysis to **cli-explore-agent**.
**Dual File System**:
- `project-tech.json`: Auto-generated technical analysis (stack, architecture, components)
- `project-guidelines.json`: User-maintained rules and constraints (created as scaffold)
- `specs/*.md`: User-maintained rules and constraints (created as scaffold)
**Note**: This command may be called by other workflow commands. Upon completion, return immediately to continue the calling workflow without interrupting the task flow.
@@ -43,7 +43,7 @@ Analysis Flow:
│ ├─ Synthesis and merge
│ └─ Write .workflow/project-tech.json
├─ Create guidelines scaffold (if not exists)
│ └─ Write .workflow/project-guidelines.json (empty structure)
│ └─ Write .workflow/specs/*.md (empty structure)
├─ Display summary
└─ Ask about guidelines configuration
├─ If guidelines empty → Ask user: "Configure now?" or "Skip"
@@ -53,7 +53,7 @@ Analysis Flow:
Output:
├─ .workflow/project-tech.json (+ .backup if regenerate)
└─ .workflow/project-guidelines.json (scaffold or configured)
└─ .workflow/specs/*.md (scaffold or configured)
```
## Implementation
@@ -69,14 +69,14 @@ const regenerate = $ARGUMENTS.includes('--regenerate')
```bash
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/coding-conventions.md && echo "SPECS_EXISTS" || echo "SPECS_NOT_FOUND")
```
**If BOTH_EXIST and no --regenerate**: Exit early
```
Project already initialized:
- Tech analysis: .workflow/project-tech.json
- Guidelines: .workflow/project-guidelines.json
- Guidelines: .workflow/specs/*.md
Use /workflow:init --regenerate to rebuild tech analysis
Use /workflow:session:solidify to add guidelines
@@ -159,33 +159,13 @@ Project root: ${projectRoot}
)
```
### Step 3.5: Create Guidelines Scaffold (if not exists)
### Step 3.5: Initialize Spec System (if not exists)
```javascript
// Only create if not exists (never overwrite user guidelines)
if (!file_exists('.workflow/project-guidelines.json')) {
const guidelinesScaffold = {
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(guidelinesScaffold, null, 2));
// Initialize spec system if not already initialized
if (!file_exists('.workflow/specs/coding-conventions.md')) {
Bash('ccw spec init');
Bash('ccw spec rebuild');
}
```
@@ -193,10 +173,10 @@ if (!file_exists('.workflow/project-guidelines.json')) {
```javascript
const projectTech = JSON.parse(Read('.workflow/project-tech.json'));
const guidelinesExists = file_exists('.workflow/project-guidelines.json');
const specsInitialized = file_exists('.workflow/specs/coding-conventions.md');
console.log(`
Project initialized successfully
Project initialized successfully
## Project Overview
Name: ${projectTech.project_name}
@@ -213,7 +193,7 @@ Components: ${projectTech.overview.key_components.length} core modules
---
Files created:
- Tech analysis: .workflow/project-tech.json
- Guidelines: .workflow/project-guidelines.json ${guidelinesExists ? '(scaffold)' : ''}
- Specs: .workflow/specs/ ${specsInitialized ? '(initialized)' : ''}
${regenerate ? '- Backup: .workflow/project-tech.json.backup' : ''}
`);
```
@@ -223,20 +203,16 @@ ${regenerate ? '- Backup: .workflow/project-tech.json.backup' : ''}
After displaying the summary, ask the user if they want to configure project guidelines interactively.
```javascript
// Check if guidelines are just a scaffold (empty) or already populated
const guidelines = JSON.parse(Read('.workflow/project-guidelines.json'));
const isGuidelinesPopulated =
guidelines.conventions.coding_style.length > 0 ||
guidelines.conventions.naming_patterns.length > 0 ||
guidelines.constraints.architecture.length > 0 ||
guidelines.constraints.security.length > 0;
// Check if specs have user content beyond seed documents
const specsList = Bash('ccw spec list --json');
const specsCount = JSON.parse(specsList).total || 0;
// Only ask if guidelines are not yet populated
if (!isGuidelinesPopulated) {
// Only ask if specs are just seeds
if (specsCount <= 5) {
const userChoice = AskUserQuestion({
questions: [{
question: "Would you like to configure project guidelines now? The wizard will ask targeted questions based on your tech stack.",
header: "Guidelines",
question: "Would you like to configure project specs now? The wizard will ask targeted questions based on your tech stack.",
header: "Specs",
multiSelect: false,
options: [
{
@@ -245,26 +221,26 @@ if (!isGuidelinesPopulated) {
},
{
label: "Skip for now",
description: "You can run /workflow:init-guidelines later or use /workflow:session:solidify to add rules individually"
description: "You can run /workflow:init-guidelines later or use ccw spec load to import specs"
}
]
}]
});
if (userChoice.answers["Guidelines"] === "Configure now (Recommended)") {
console.log("\n🔧 Starting guidelines configuration wizard...\n");
if (userChoice.answers["Specs"] === "Configure now (Recommended)") {
console.log("\nStarting specs configuration wizard...\n");
Skill(skill="workflow:init-guidelines");
} else {
console.log(`
Next steps:
- Use /workflow:init-guidelines to configure guidelines interactively
- Use /workflow:session:solidify to add individual rules
- Use /workflow:init-guidelines to configure specs interactively
- Use ccw spec load to import specs from external sources
- Use /workflow:plan to start planning
`);
}
} else {
console.log(`
Guidelines already configured (${guidelines.conventions.coding_style.length + guidelines.constraints.architecture.length}+ rules).
Specs already configured (${specsCount} spec files).
Next steps:
- Use /workflow:init-guidelines --reset to reconfigure

View File

@@ -843,7 +843,7 @@ AskUserQuestion({
## Post-Completion Expansion
**Auto-sync**: 执行 `/workflow:session:sync -y "{summary}"` 更新 project-guidelines + project-tech。
**Auto-sync**: 执行 `/workflow:session:sync -y "{summary}"` 更新 specs/*.md + project-tech。
完成后询问用户是否扩展为issue(test/enhance/refactor/doc),选中项调用 `/issue:new "{summary} - {dimension}"`

View File

@@ -347,7 +347,7 @@ Bash(`mkdir -p ${sessionFolder}`)
1. Run: ccw tool exec get_modules_by_depth '{}'
2. Execute relevant searches based on requirement keywords
3. Read: .workflow/project-tech.json (if exists)
4. Read: .workflow/project-guidelines.json (if exists)
4. Read: .workflow/specs/*.md (if exists)
## Exploration Focus
- Identify modules/components related to the requirement

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 ──

View File

@@ -476,9 +476,9 @@ ${recommendations.map(r => \`- ${r}\`).join('\\n')}
// Read project-tech.json (if exists)
const projectTech = file_exists('.workflow/project-tech.json')
? JSON.parse(Read('.workflow/project-tech.json')) : null
// Read project-guidelines.json (if exists)
const projectGuidelines = file_exists('.workflow/project-guidelines.json')
? JSON.parse(Read('.workflow/project-guidelines.json')) : null
// Read specs/*.md (if exists)
const projectGuidelines = file_exists('.workflow/specs/*.md')
? JSON.parse(Read('.workflow/specs/*.md')) : null
```
```javascript