mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-10 02:24:35 +08:00
feat: 增强 CSS 布局,优化组件的灵活性和响应式设计;更新调试和轻量级计划工作流文档
This commit is contained in:
318
.codex/prompts/debug.md
Normal file
318
.codex/prompts/debug.md
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
---
|
||||||
|
description: Interactive hypothesis-driven debugging with NDJSON logging, iterative until resolved
|
||||||
|
argument-hint: BUG="<bug description or error message>"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Workflow Debug Command
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Evidence-based interactive debugging command. Systematically identifies root causes through hypothesis-driven logging and iterative verification.
|
||||||
|
|
||||||
|
**Core workflow**: Explore → Add Logging → Reproduce → Analyze Log → Fix → Verify
|
||||||
|
|
||||||
|
## Bug Description
|
||||||
|
|
||||||
|
**Target bug**: $BUG
|
||||||
|
|
||||||
|
## Execution Process
|
||||||
|
|
||||||
|
```
|
||||||
|
Session Detection:
|
||||||
|
├─ Check if debug session exists for this bug
|
||||||
|
├─ EXISTS + debug.log has content → Analyze mode
|
||||||
|
└─ NOT_FOUND or empty log → Explore mode
|
||||||
|
|
||||||
|
Explore Mode:
|
||||||
|
├─ Locate error source in codebase
|
||||||
|
├─ Generate testable hypotheses (dynamic count)
|
||||||
|
├─ Add NDJSON logging instrumentation
|
||||||
|
└─ Output: Hypothesis list + await user reproduction
|
||||||
|
|
||||||
|
Analyze Mode:
|
||||||
|
├─ Parse debug.log, validate each hypothesis
|
||||||
|
└─ Decision:
|
||||||
|
├─ Confirmed → Fix root cause
|
||||||
|
├─ Inconclusive → Add more logging, iterate
|
||||||
|
└─ All rejected → Generate new hypotheses
|
||||||
|
|
||||||
|
Fix & Cleanup:
|
||||||
|
├─ Apply fix based on confirmed hypothesis
|
||||||
|
├─ User verifies
|
||||||
|
├─ Remove debug instrumentation
|
||||||
|
└─ If not fixed → Return to Analyze mode
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
### Session Setup & Mode Detection
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
|
||||||
|
|
||||||
|
const bugSlug = "$BUG".toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 30)
|
||||||
|
const dateStr = getUtc8ISOString().substring(0, 10)
|
||||||
|
|
||||||
|
const sessionId = `DBG-${bugSlug}-${dateStr}`
|
||||||
|
const sessionFolder = `.workflow/.debug/${sessionId}`
|
||||||
|
const debugLogPath = `${sessionFolder}/debug.log`
|
||||||
|
|
||||||
|
// Auto-detect mode
|
||||||
|
const sessionExists = fs.existsSync(sessionFolder)
|
||||||
|
const logHasContent = sessionExists && fs.existsSync(debugLogPath) && fs.statSync(debugLogPath).size > 0
|
||||||
|
|
||||||
|
const mode = logHasContent ? 'analyze' : 'explore'
|
||||||
|
|
||||||
|
if (!sessionExists) {
|
||||||
|
bash(`mkdir -p ${sessionFolder}`)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Explore Mode
|
||||||
|
|
||||||
|
**Step 1.1: Locate Error Source**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Extract keywords from bug description
|
||||||
|
const keywords = extractErrorKeywords("$BUG")
|
||||||
|
// e.g., ['Stack Length', '未找到', 'registered 0']
|
||||||
|
|
||||||
|
// Search codebase for error locations
|
||||||
|
for (const keyword of keywords) {
|
||||||
|
Grep({ pattern: keyword, path: ".", output_mode: "content", "-C": 3 })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identify affected files and functions
|
||||||
|
const affectedLocations = [...] // from search results
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 1.2: Generate Hypotheses (Dynamic)**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Hypothesis categories based on error pattern
|
||||||
|
const HYPOTHESIS_PATTERNS = {
|
||||||
|
"not found|missing|undefined|未找到": "data_mismatch",
|
||||||
|
"0|empty|zero|registered 0": "logic_error",
|
||||||
|
"timeout|connection|sync": "integration_issue",
|
||||||
|
"type|format|parse": "type_mismatch"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate hypotheses based on actual issue (NOT fixed count)
|
||||||
|
function generateHypotheses(bugDescription, affectedLocations) {
|
||||||
|
const hypotheses = []
|
||||||
|
|
||||||
|
// Analyze bug and create targeted hypotheses
|
||||||
|
// Each hypothesis has:
|
||||||
|
// - id: H1, H2, ... (dynamic count)
|
||||||
|
// - description: What might be wrong
|
||||||
|
// - testable_condition: What to log
|
||||||
|
// - logging_point: Where to add instrumentation
|
||||||
|
|
||||||
|
return hypotheses // Could be 1, 3, 5, or more
|
||||||
|
}
|
||||||
|
|
||||||
|
const hypotheses = generateHypotheses("$BUG", affectedLocations)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 1.3: Add NDJSON Instrumentation**
|
||||||
|
|
||||||
|
For each hypothesis, add logging at the relevant location:
|
||||||
|
|
||||||
|
**Python template**:
|
||||||
|
```python
|
||||||
|
# region debug [H{n}]
|
||||||
|
try:
|
||||||
|
import json, time
|
||||||
|
_dbg = {
|
||||||
|
"sid": "{sessionId}",
|
||||||
|
"hid": "H{n}",
|
||||||
|
"loc": "{file}:{line}",
|
||||||
|
"msg": "{testable_condition}",
|
||||||
|
"data": {
|
||||||
|
# Capture relevant values here
|
||||||
|
},
|
||||||
|
"ts": int(time.time() * 1000)
|
||||||
|
}
|
||||||
|
with open(r"{debugLogPath}", "a", encoding="utf-8") as _f:
|
||||||
|
_f.write(json.dumps(_dbg, ensure_ascii=False) + "\n")
|
||||||
|
except: pass
|
||||||
|
# endregion
|
||||||
|
```
|
||||||
|
|
||||||
|
**JavaScript/TypeScript template**:
|
||||||
|
```javascript
|
||||||
|
// region debug [H{n}]
|
||||||
|
try {
|
||||||
|
require('fs').appendFileSync("{debugLogPath}", JSON.stringify({
|
||||||
|
sid: "{sessionId}",
|
||||||
|
hid: "H{n}",
|
||||||
|
loc: "{file}:{line}",
|
||||||
|
msg: "{testable_condition}",
|
||||||
|
data: { /* Capture relevant values */ },
|
||||||
|
ts: Date.now()
|
||||||
|
}) + "\n");
|
||||||
|
} catch(_) {}
|
||||||
|
// endregion
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output to user**:
|
||||||
|
```
|
||||||
|
## Hypotheses Generated
|
||||||
|
|
||||||
|
Based on error "$BUG", generated {n} hypotheses:
|
||||||
|
|
||||||
|
{hypotheses.map(h => `
|
||||||
|
### ${h.id}: ${h.description}
|
||||||
|
- Logging at: ${h.logging_point}
|
||||||
|
- Testing: ${h.testable_condition}
|
||||||
|
`).join('')}
|
||||||
|
|
||||||
|
**Debug log**: ${debugLogPath}
|
||||||
|
|
||||||
|
**Next**: Run reproduction steps, then come back for analysis.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Analyze Mode
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Parse NDJSON log
|
||||||
|
const entries = Read(debugLogPath).split('\n')
|
||||||
|
.filter(l => l.trim())
|
||||||
|
.map(l => JSON.parse(l))
|
||||||
|
|
||||||
|
// Group by hypothesis
|
||||||
|
const byHypothesis = groupBy(entries, 'hid')
|
||||||
|
|
||||||
|
// Validate each hypothesis
|
||||||
|
for (const [hid, logs] of Object.entries(byHypothesis)) {
|
||||||
|
const hypothesis = hypotheses.find(h => h.id === hid)
|
||||||
|
const latestLog = logs[logs.length - 1]
|
||||||
|
|
||||||
|
// Check if evidence confirms or rejects hypothesis
|
||||||
|
const verdict = evaluateEvidence(hypothesis, latestLog.data)
|
||||||
|
// Returns: 'confirmed' | 'rejected' | 'inconclusive'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output**:
|
||||||
|
```
|
||||||
|
## Evidence Analysis
|
||||||
|
|
||||||
|
Analyzed ${entries.length} log entries.
|
||||||
|
|
||||||
|
${results.map(r => `
|
||||||
|
### ${r.id}: ${r.description}
|
||||||
|
- **Status**: ${r.verdict}
|
||||||
|
- **Evidence**: ${JSON.stringify(r.evidence)}
|
||||||
|
- **Reason**: ${r.reason}
|
||||||
|
`).join('')}
|
||||||
|
|
||||||
|
${confirmedHypothesis ? `
|
||||||
|
## Root Cause Identified
|
||||||
|
|
||||||
|
**${confirmedHypothesis.id}**: ${confirmedHypothesis.description}
|
||||||
|
|
||||||
|
Ready to fix.
|
||||||
|
` : `
|
||||||
|
## Need More Evidence
|
||||||
|
|
||||||
|
Add more logging or refine hypotheses.
|
||||||
|
`}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Fix & Cleanup
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Apply fix based on confirmed hypothesis
|
||||||
|
// ... Edit affected files
|
||||||
|
|
||||||
|
// After user verifies fix works:
|
||||||
|
|
||||||
|
// Remove debug instrumentation (search for region markers)
|
||||||
|
const instrumentedFiles = Grep({
|
||||||
|
pattern: "# region debug|// region debug",
|
||||||
|
output_mode: "files_with_matches"
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const file of instrumentedFiles) {
|
||||||
|
// Remove content between region markers
|
||||||
|
removeDebugRegions(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
## Debug Complete
|
||||||
|
|
||||||
|
- Root cause: ${confirmedHypothesis.description}
|
||||||
|
- Fix applied to: ${modifiedFiles.join(', ')}
|
||||||
|
- Debug instrumentation removed
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Debug Log Format (NDJSON)
|
||||||
|
|
||||||
|
Each line is a JSON object:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"sid":"DBG-xxx-2025-12-18","hid":"H1","loc":"file.py:func:42","msg":"Check dict keys","data":{"keys":["a","b"],"target":"c","found":false},"ts":1734567890123}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `sid` | Session ID |
|
||||||
|
| `hid` | Hypothesis ID (H1, H2, ...) |
|
||||||
|
| `loc` | Code location |
|
||||||
|
| `msg` | What's being tested |
|
||||||
|
| `data` | Captured values |
|
||||||
|
| `ts` | Timestamp (ms) |
|
||||||
|
|
||||||
|
## Session Folder
|
||||||
|
|
||||||
|
```
|
||||||
|
.workflow/.debug/DBG-{slug}-{date}/
|
||||||
|
├── debug.log # NDJSON log (main artifact)
|
||||||
|
└── resolution.md # Summary after fix (optional)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Iteration Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
First Call (/prompts:debug BUG="error"):
|
||||||
|
├─ No session exists → Explore mode
|
||||||
|
├─ Extract error keywords, search codebase
|
||||||
|
├─ Generate hypotheses, add logging
|
||||||
|
└─ Await user reproduction
|
||||||
|
|
||||||
|
After Reproduction (/prompts:debug BUG="error"):
|
||||||
|
├─ Session exists + debug.log has content → Analyze mode
|
||||||
|
├─ Parse log, evaluate hypotheses
|
||||||
|
└─ Decision:
|
||||||
|
├─ Confirmed → Fix → User verify
|
||||||
|
│ ├─ Fixed → Cleanup → Done
|
||||||
|
│ └─ Not fixed → Add logging → Iterate
|
||||||
|
├─ Inconclusive → Add logging → Iterate
|
||||||
|
└─ All rejected → New hypotheses → Iterate
|
||||||
|
|
||||||
|
Output:
|
||||||
|
└─ .workflow/.debug/DBG-{slug}-{date}/debug.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Situation | Action |
|
||||||
|
|-----------|--------|
|
||||||
|
| Empty debug.log | Verify reproduction triggered the code path |
|
||||||
|
| All hypotheses rejected | Generate new hypotheses with broader scope |
|
||||||
|
| Fix doesn't work | Iterate with more granular logging |
|
||||||
|
| >5 iterations | Escalate with collected evidence |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Now execute the debug workflow for bug**: $BUG
|
||||||
469
.codex/prompts/lite-plan.md
Normal file
469
.codex/prompts/lite-plan.md
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
---
|
||||||
|
description: Lightweight interactive planning workflow with direct exploration, outputs plan.json after user confirmation
|
||||||
|
argument-hint: TASK="<task description or file.md path>" [EXPLORE="true"]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Workflow Lite-Plan Command
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Intelligent lightweight planning command with dynamic workflow adaptation based on task complexity. Focuses on planning phases (exploration, clarification, planning, confirmation) and outputs plan.json for subsequent execution.
|
||||||
|
|
||||||
|
**Core capabilities:**
|
||||||
|
- Intelligent task analysis with automatic exploration detection
|
||||||
|
- Direct code exploration (grep, find, file reading) when codebase understanding needed
|
||||||
|
- Interactive clarification after exploration to gather missing information
|
||||||
|
- Adaptive planning strategy based on complexity
|
||||||
|
- Two-step confirmation: plan display → user approval
|
||||||
|
- Outputs plan.json file after user confirmation
|
||||||
|
|
||||||
|
## Task Description
|
||||||
|
|
||||||
|
**Target task**: $TASK
|
||||||
|
**Force exploration**: $EXPLORE
|
||||||
|
|
||||||
|
## Execution Process
|
||||||
|
|
||||||
|
```
|
||||||
|
Phase 1: Task Analysis & Exploration
|
||||||
|
├─ Parse input (description or .md file)
|
||||||
|
├─ Intelligent complexity assessment (Low/Medium/High)
|
||||||
|
├─ Exploration decision (auto-detect or EXPLORE="true")
|
||||||
|
└─ Decision:
|
||||||
|
├─ needsExploration=true → Direct exploration using grep/find/read
|
||||||
|
└─ needsExploration=false → Skip to Phase 2/3
|
||||||
|
|
||||||
|
Phase 2: Clarification (optional)
|
||||||
|
├─ Aggregate clarification needs from exploration
|
||||||
|
├─ Output questions to user
|
||||||
|
└─ STOP and wait for user reply
|
||||||
|
|
||||||
|
Phase 3: Planning (NO CODE EXECUTION - planning only)
|
||||||
|
└─ Generate plan.json following schema
|
||||||
|
└─ MUST proceed to Phase 4
|
||||||
|
|
||||||
|
Phase 4: Confirmation
|
||||||
|
├─ Display plan summary (tasks, complexity, estimated time)
|
||||||
|
├─ Output confirmation request
|
||||||
|
└─ STOP and wait for user approval
|
||||||
|
|
||||||
|
Phase 5: Output
|
||||||
|
└─ Write plan.json to session folder
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
### Phase 1: Intelligent Direct Exploration
|
||||||
|
|
||||||
|
**Session Setup** (MANDATORY - follow exactly):
|
||||||
|
```javascript
|
||||||
|
// Helper: Get UTC+8 (China Standard Time) ISO string
|
||||||
|
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
|
||||||
|
|
||||||
|
const taskSlug = "$TASK".toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40)
|
||||||
|
const dateStr = getUtc8ISOString().substring(0, 10) // Format: 2025-11-29
|
||||||
|
|
||||||
|
const sessionId = `${taskSlug}-${dateStr}` // e.g., "implement-jwt-refresh-2025-11-29"
|
||||||
|
const sessionFolder = `.workflow/.lite-plan/${sessionId}`
|
||||||
|
|
||||||
|
// Create session folder
|
||||||
|
mkdir -p ${sessionFolder}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Exploration Decision Logic**:
|
||||||
|
```javascript
|
||||||
|
needsExploration = (
|
||||||
|
"$EXPLORE" === "true" ||
|
||||||
|
task.mentions_specific_files ||
|
||||||
|
task.requires_codebase_context ||
|
||||||
|
task.needs_architecture_understanding ||
|
||||||
|
task.modifies_existing_code
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!needsExploration) {
|
||||||
|
// Skip to Phase 2 (Clarification) or Phase 3 (Planning)
|
||||||
|
proceed_to_next_phase()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Complexity Assessment** (Intelligent Analysis):
|
||||||
|
```javascript
|
||||||
|
// Analyzes task complexity based on:
|
||||||
|
// - Scope: How many systems/modules are affected?
|
||||||
|
// - Depth: Surface change vs architectural impact?
|
||||||
|
// - Risk: Potential for breaking existing functionality?
|
||||||
|
// - Dependencies: How interconnected is the change?
|
||||||
|
|
||||||
|
const complexity = analyzeTaskComplexity("$TASK")
|
||||||
|
// Returns: 'Low' | 'Medium' | 'High'
|
||||||
|
// Low: Single file, isolated change, minimal risk
|
||||||
|
// Medium: Multiple files, some dependencies, moderate risk
|
||||||
|
// High: Cross-module, architectural, high risk
|
||||||
|
|
||||||
|
// Angle assignment based on task type
|
||||||
|
const ANGLE_PRESETS = {
|
||||||
|
architecture: ['architecture', 'dependencies', 'modularity', 'integration-points'],
|
||||||
|
security: ['security', 'auth-patterns', 'dataflow', 'validation'],
|
||||||
|
performance: ['performance', 'bottlenecks', 'caching', 'data-access'],
|
||||||
|
bugfix: ['error-handling', 'dataflow', 'state-management', 'edge-cases'],
|
||||||
|
feature: ['patterns', 'integration-points', 'testing', 'dependencies']
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAngles(taskDescription, count) {
|
||||||
|
const text = taskDescription.toLowerCase()
|
||||||
|
let preset = 'feature' // default
|
||||||
|
|
||||||
|
if (/refactor|architect|restructure|modular/.test(text)) preset = 'architecture'
|
||||||
|
else if (/security|auth|permission|access/.test(text)) preset = 'security'
|
||||||
|
else if (/performance|slow|optimi|cache/.test(text)) preset = 'performance'
|
||||||
|
else if (/fix|bug|error|issue|broken/.test(text)) preset = 'bugfix'
|
||||||
|
|
||||||
|
return ANGLE_PRESETS[preset].slice(0, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedAngles = selectAngles("$TASK", complexity === 'High' ? 4 : (complexity === 'Medium' ? 3 : 1))
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
## Exploration Plan
|
||||||
|
|
||||||
|
Task Complexity: ${complexity}
|
||||||
|
Selected Angles: ${selectedAngles.join(', ')}
|
||||||
|
|
||||||
|
Starting direct exploration...
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Direct Exploration** (No Agent - Use grep/find/read directly):
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// For each selected angle, perform direct exploration
|
||||||
|
|
||||||
|
selectedAngles.forEach((angle, index) => {
|
||||||
|
console.log(`\n### Exploring: ${angle} (${index + 1}/${selectedAngles.length})`)
|
||||||
|
|
||||||
|
// Step 1: Structural Scan
|
||||||
|
// - Find relevant files using grep/rg
|
||||||
|
// - Analyze directory structure
|
||||||
|
// - Identify modules related to the angle
|
||||||
|
|
||||||
|
// Example commands:
|
||||||
|
// rg -l "keyword_from_task" --type ts
|
||||||
|
// find . -name "*.ts" -path "*auth*"
|
||||||
|
// tree -L 3 src/
|
||||||
|
|
||||||
|
// Step 2: Content Analysis
|
||||||
|
// - Read key files identified
|
||||||
|
// - Analyze patterns and conventions
|
||||||
|
// - Identify integration points
|
||||||
|
|
||||||
|
// Step 3: Document Findings
|
||||||
|
const explorationResult = {
|
||||||
|
angle: angle,
|
||||||
|
project_structure: [], // Modules/architecture relevant to angle
|
||||||
|
relevant_files: [], // Files affected from angle perspective
|
||||||
|
patterns: [], // Angle-related patterns to follow
|
||||||
|
dependencies: [], // Dependencies relevant to angle
|
||||||
|
integration_points: [], // Where to integrate from angle viewpoint
|
||||||
|
constraints: [], // Angle-specific limitations/conventions
|
||||||
|
clarification_needs: [], // Angle-related ambiguities
|
||||||
|
_metadata: {
|
||||||
|
exploration_angle: angle,
|
||||||
|
exploration_index: index + 1,
|
||||||
|
timestamp: getUtc8ISOString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write exploration result
|
||||||
|
Write(`${sessionFolder}/exploration-${angle}.json`, JSON.stringify(explorationResult, null, 2))
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Build Exploration Manifest**:
|
||||||
|
```javascript
|
||||||
|
// After all explorations complete, build manifest
|
||||||
|
const explorationFiles = find(`${sessionFolder}`, "-name", "exploration-*.json")
|
||||||
|
|
||||||
|
const explorationManifest = {
|
||||||
|
session_id: sessionId,
|
||||||
|
task_description: "$TASK",
|
||||||
|
timestamp: getUtc8ISOString(),
|
||||||
|
complexity: complexity,
|
||||||
|
exploration_count: selectedAngles.length,
|
||||||
|
explorations: explorationFiles.map(file => {
|
||||||
|
const data = JSON.parse(Read(file))
|
||||||
|
return {
|
||||||
|
angle: data._metadata.exploration_angle,
|
||||||
|
file: path.basename(file),
|
||||||
|
path: file,
|
||||||
|
index: data._metadata.exploration_index
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Write(`${sessionFolder}/explorations-manifest.json`, JSON.stringify(explorationManifest, null, 2))
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
## Exploration Complete
|
||||||
|
|
||||||
|
Generated exploration files in ${sessionFolder}:
|
||||||
|
${explorationManifest.explorations.map(e => `- exploration-${e.angle}.json (angle: ${e.angle})`).join('\n')}
|
||||||
|
|
||||||
|
Manifest: explorations-manifest.json
|
||||||
|
Angles explored: ${explorationManifest.explorations.map(e => e.angle).join(', ')}
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output**:
|
||||||
|
- `${sessionFolder}/exploration-{angle1}.json`
|
||||||
|
- `${sessionFolder}/exploration-{angle2}.json`
|
||||||
|
- ... (1-4 files based on complexity)
|
||||||
|
- `${sessionFolder}/explorations-manifest.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 2: Clarification (Optional)
|
||||||
|
|
||||||
|
**Skip if**: No exploration or `clarification_needs` is empty across all explorations
|
||||||
|
|
||||||
|
**Aggregate clarification needs from all exploration angles**:
|
||||||
|
```javascript
|
||||||
|
// Load manifest and all exploration files
|
||||||
|
const manifest = JSON.parse(Read(`${sessionFolder}/explorations-manifest.json`))
|
||||||
|
const explorations = manifest.explorations.map(exp => ({
|
||||||
|
angle: exp.angle,
|
||||||
|
data: JSON.parse(Read(exp.path))
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Aggregate clarification needs from all explorations
|
||||||
|
const allClarifications = []
|
||||||
|
explorations.forEach(exp => {
|
||||||
|
if (exp.data.clarification_needs?.length > 0) {
|
||||||
|
exp.data.clarification_needs.forEach(need => {
|
||||||
|
allClarifications.push({
|
||||||
|
...need,
|
||||||
|
source_angle: exp.angle
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Intelligent deduplication: analyze allClarifications by intent
|
||||||
|
// - Identify questions with similar intent across different angles
|
||||||
|
// - Merge similar questions: combine options, consolidate context
|
||||||
|
// - Produce dedupedClarifications with unique intents only
|
||||||
|
const dedupedClarifications = intelligentMerge(allClarifications)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output Questions and Wait for User Reply**:
|
||||||
|
```javascript
|
||||||
|
if (dedupedClarifications.length > 0) {
|
||||||
|
console.log(`
|
||||||
|
## Clarification Needed
|
||||||
|
|
||||||
|
Based on exploration, the following questions need your input:
|
||||||
|
|
||||||
|
${dedupedClarifications.map((need, index) => `
|
||||||
|
### Question ${index + 1}: [${need.source_angle}]
|
||||||
|
|
||||||
|
**${need.question}**
|
||||||
|
|
||||||
|
Context: ${need.context}
|
||||||
|
|
||||||
|
Options:
|
||||||
|
${need.options.map((opt, i) => ` ${i + 1}. ${opt}${need.recommended === i ? ' ★ (Recommended)' : ''}`).join('\n')}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Please reply with your choices** (e.g., "Q1: 2, Q2: 1, Q3: 3") to continue planning.
|
||||||
|
|
||||||
|
**WAITING FOR USER INPUT...**
|
||||||
|
`)
|
||||||
|
|
||||||
|
// STOP HERE - Wait for user reply before continuing to Phase 3
|
||||||
|
return
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**After User Reply**: Store responses in `clarificationContext` and proceed to Phase 3.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 3: Planning
|
||||||
|
|
||||||
|
**IMPORTANT**: Phase 3 is **planning only** - NO code execution.
|
||||||
|
|
||||||
|
**Read Schema**:
|
||||||
|
```javascript
|
||||||
|
// Read plan schema for reference
|
||||||
|
const schema = Read("~/.claude/workflows/cli-templates/schemas/plan-json-schema.json")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Read All Exploration Files**:
|
||||||
|
```javascript
|
||||||
|
// MANDATORY - Read and review ALL exploration files
|
||||||
|
const manifest = JSON.parse(Read(`${sessionFolder}/explorations-manifest.json`))
|
||||||
|
manifest.explorations.forEach(exp => {
|
||||||
|
const explorationData = Read(exp.path)
|
||||||
|
console.log(`\n### Exploration: ${exp.angle}\n${explorationData}`)
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Generate Plan**:
|
||||||
|
```javascript
|
||||||
|
// Generate plan following schema
|
||||||
|
// Plan MUST incorporate insights from exploration files
|
||||||
|
const plan = {
|
||||||
|
summary: "Brief description of what will be implemented",
|
||||||
|
approach: "High-level approach and strategy",
|
||||||
|
tasks: [
|
||||||
|
// Each task: { id, title, description, scope, files, depends_on, execution_group, complexity }
|
||||||
|
// Group by feature/module, NOT by file
|
||||||
|
// 2-7 tasks recommended
|
||||||
|
],
|
||||||
|
estimated_time: "Total estimated time",
|
||||||
|
complexity: complexity, // Low | Medium | High
|
||||||
|
_metadata: {
|
||||||
|
timestamp: getUtc8ISOString(),
|
||||||
|
source: "lite-plan",
|
||||||
|
planning_mode: "direct",
|
||||||
|
exploration_angles: manifest.explorations.map(e => e.angle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Task Grouping Rules**:
|
||||||
|
1. **Group by feature**: All changes for one feature = one task (even if 3-5 files)
|
||||||
|
2. **Group by context**: Tasks with similar context or related functional changes can be grouped together
|
||||||
|
3. **Minimize task count**: Simple, unrelated tasks can also be grouped to reduce overhead
|
||||||
|
4. **Avoid file-per-task**: Do NOT create separate tasks for each file
|
||||||
|
5. **Substantial tasks**: Each task should represent 15-60 minutes of work
|
||||||
|
6. **True dependencies only**: Only use depends_on when Task B cannot start without Task A's output
|
||||||
|
7. **Prefer parallel**: Most tasks should be independent (no depends_on)
|
||||||
|
|
||||||
|
**Proceed to Phase 4** - DO NOT execute code here.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 4: Task Confirmation
|
||||||
|
|
||||||
|
**Display Plan Summary**:
|
||||||
|
```javascript
|
||||||
|
const plan = JSON.parse(Read(`${sessionFolder}/plan.json`))
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
**Summary**: ${plan.summary}
|
||||||
|
**Approach**: ${plan.approach}
|
||||||
|
|
||||||
|
**Tasks** (${plan.tasks.length}):
|
||||||
|
${plan.tasks.map((t, i) => `
|
||||||
|
### Task ${i+1}: ${t.title}
|
||||||
|
- **Description**: ${t.description}
|
||||||
|
- **Scope**: ${t.scope}
|
||||||
|
- **Files**: ${t.files.join(', ')}
|
||||||
|
- **Complexity**: ${t.complexity}
|
||||||
|
- **Dependencies**: ${t.depends_on?.join(', ') || 'None'}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
**Overall Complexity**: ${plan.complexity}
|
||||||
|
**Estimated Time**: ${plan.estimated_time}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Confirmation Required
|
||||||
|
|
||||||
|
Please review the plan above and reply with one of the following:
|
||||||
|
|
||||||
|
- **"Allow"** - Proceed with this plan, output plan.json
|
||||||
|
- **"Modify"** - Describe what changes you want to make
|
||||||
|
- **"Cancel"** - Abort the planning workflow
|
||||||
|
|
||||||
|
**WAITING FOR USER CONFIRMATION...**
|
||||||
|
`)
|
||||||
|
|
||||||
|
// STOP HERE - Wait for user confirmation before writing plan.json
|
||||||
|
return
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 5: Output Plan File
|
||||||
|
|
||||||
|
**After User Confirms "Allow"**:
|
||||||
|
```javascript
|
||||||
|
// Write final plan.json to session folder
|
||||||
|
Write(`${sessionFolder}/plan.json`, JSON.stringify(plan, null, 2))
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
## Plan Output Complete
|
||||||
|
|
||||||
|
**Plan file written**: ${sessionFolder}/plan.json
|
||||||
|
|
||||||
|
**Session folder**: ${sessionFolder}
|
||||||
|
|
||||||
|
**Contents**:
|
||||||
|
- explorations-manifest.json
|
||||||
|
${manifest.explorations.map(e => `- exploration-${e.angle}.json`).join('\n')}
|
||||||
|
- plan.json
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
You can now use this plan with your preferred execution method:
|
||||||
|
- Manual implementation following the tasks
|
||||||
|
- Pass to another tool/agent for execution
|
||||||
|
- Import into project management system
|
||||||
|
`)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session Folder Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
.workflow/.lite-plan/{task-slug}-{YYYY-MM-DD}/
|
||||||
|
├── exploration-{angle1}.json # Exploration angle 1
|
||||||
|
├── exploration-{angle2}.json # Exploration angle 2
|
||||||
|
├── exploration-{angle3}.json # Exploration angle 3 (if applicable)
|
||||||
|
├── exploration-{angle4}.json # Exploration angle 4 (if applicable)
|
||||||
|
├── explorations-manifest.json # Exploration index
|
||||||
|
└── plan.json # Implementation plan (after confirmation)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```
|
||||||
|
.workflow/.lite-plan/implement-jwt-refresh-2025-11-25/
|
||||||
|
├── exploration-architecture.json
|
||||||
|
├── exploration-auth-patterns.json
|
||||||
|
├── exploration-security.json
|
||||||
|
├── explorations-manifest.json
|
||||||
|
└── plan.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow States
|
||||||
|
|
||||||
|
| State | Action | Next |
|
||||||
|
|-------|--------|------|
|
||||||
|
| Phase 1 Complete | Exploration done | → Phase 2 or 3 |
|
||||||
|
| Phase 2 Output | Questions displayed | → Wait for user reply |
|
||||||
|
| User Replied | Clarifications received | → Phase 3 |
|
||||||
|
| Phase 3 Complete | Plan generated | → Phase 4 |
|
||||||
|
| Phase 4 Output | Plan displayed | → Wait for user confirmation |
|
||||||
|
| User: "Allow" | Confirmed | → Phase 5 (Write plan.json) |
|
||||||
|
| User: "Modify" | Changes requested | → Revise plan, back to Phase 4 |
|
||||||
|
| User: "Cancel" | Aborted | → End workflow |
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Error | Resolution |
|
||||||
|
|-------|------------|
|
||||||
|
| Exploration failure | Skip exploration, continue with task description only |
|
||||||
|
| No relevant files found | Broaden search scope or proceed with minimal context |
|
||||||
|
| Clarification timeout | Use exploration findings as-is |
|
||||||
|
| Confirmation timeout | Save context, display resume instructions |
|
||||||
|
| Modify loop > 3 times | Suggest breaking task into smaller pieces |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Now execute the lite-plan workflow for task**: $TASK
|
||||||
@@ -4,10 +4,15 @@
|
|||||||
.prompt-history-view {
|
.prompt-history-view {
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prompt-history-header {
|
.prompt-history-header {
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stats Grid */
|
/* Stats Grid */
|
||||||
@@ -107,6 +112,9 @@
|
|||||||
border: 1px solid hsl(var(--border));
|
border: 1px solid hsl(var(--border));
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prompt-timeline-header {
|
.prompt-timeline-header {
|
||||||
@@ -118,6 +126,7 @@
|
|||||||
background: hsl(var(--muted) / 0.3);
|
background: hsl(var(--muted) / 0.3);
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prompt-timeline-header h3 {
|
.prompt-timeline-header h3 {
|
||||||
@@ -190,9 +199,10 @@
|
|||||||
|
|
||||||
/* Timeline List */
|
/* Timeline List */
|
||||||
.prompt-timeline-list {
|
.prompt-timeline-list {
|
||||||
max-height: 600px;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Session Groups */
|
/* Session Groups */
|
||||||
@@ -428,6 +438,9 @@
|
|||||||
border: 1px solid hsl(var(--border));
|
border: 1px solid hsl(var(--border));
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.insights-panel-header {
|
.insights-panel-header {
|
||||||
@@ -437,6 +450,7 @@
|
|||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-bottom: 1px solid hsl(var(--border));
|
border-bottom: 1px solid hsl(var(--border));
|
||||||
background: hsl(var(--muted) / 0.3);
|
background: hsl(var(--muted) / 0.3);
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.insights-panel-header h3 {
|
.insights-panel-header h3 {
|
||||||
@@ -496,6 +510,8 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 3rem 1.5rem;
|
padding: 3rem 1.5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.insights-empty-state i {
|
.insights-empty-state i {
|
||||||
@@ -514,9 +530,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.insights-list {
|
.insights-list {
|
||||||
max-height: 600px;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.insights-section {
|
.insights-section {
|
||||||
@@ -729,6 +746,8 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 3rem 1.5rem;
|
padding: 3rem 1.5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prompt-empty-state i {
|
.prompt-empty-state i {
|
||||||
@@ -752,8 +771,9 @@
|
|||||||
/* ========== Insights History Cards ========== */
|
/* ========== Insights History Cards ========== */
|
||||||
.insights-history-container {
|
.insights-history-container {
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
max-height: calc(100vh - 300px);
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.insights-history-cards {
|
.insights-history-cards {
|
||||||
|
|||||||
@@ -590,8 +590,8 @@ async function openHookWizardModal(wizardId) {
|
|||||||
wizardConfig.selectedOptions = [];
|
wizardConfig.selectedOptions = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure available skills are loaded for SKILL context wizard
|
// Always refresh available skills when opening SKILL context wizard
|
||||||
if (wizardId === 'skill-context' && typeof window.availableSkills === 'undefined') {
|
if (wizardId === 'skill-context') {
|
||||||
await loadAvailableSkills();
|
await loadAvailableSkills();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,7 +862,8 @@ function renderSkillContextConfig() {
|
|||||||
|
|
||||||
if (selectedOption === 'auto') {
|
if (selectedOption === 'auto') {
|
||||||
let skillBadges = '';
|
let skillBadges = '';
|
||||||
if (typeof window.availableSkills === 'undefined') {
|
let isLoading = typeof window.availableSkills === 'undefined' || window.skillsLoading;
|
||||||
|
if (isLoading) {
|
||||||
// Still loading
|
// Still loading
|
||||||
skillBadges = '<span class="px-1.5 py-0.5 bg-muted text-muted-foreground rounded text-xs">' + t('common.loading') + '...</span>';
|
skillBadges = '<span class="px-1.5 py-0.5 bg-muted text-muted-foreground rounded text-xs">' + t('common.loading') + '...</span>';
|
||||||
} else if (availableSkills.length === 0) {
|
} else if (availableSkills.length === 0) {
|
||||||
@@ -875,12 +876,22 @@ function renderSkillContextConfig() {
|
|||||||
}).join(' ');
|
}).join(' ');
|
||||||
}
|
}
|
||||||
return '<div class="bg-muted/30 rounded-lg p-4 text-sm text-muted-foreground">' +
|
return '<div class="bg-muted/30 rounded-lg p-4 text-sm text-muted-foreground">' +
|
||||||
'<div class="flex items-center gap-2 mb-2">' +
|
'<div class="flex items-center justify-between mb-2">' +
|
||||||
'<i data-lucide="info" class="w-4 h-4"></i>' +
|
'<div class="flex items-center gap-2">' +
|
||||||
'<span class="font-medium">' + t('hook.wizard.autoDetectionMode') + '</span>' +
|
'<i data-lucide="info" class="w-4 h-4"></i>' +
|
||||||
|
'<span class="font-medium">' + t('hook.wizard.autoDetectionMode') + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<button type="button" onclick="refreshAvailableSkills()" ' +
|
||||||
|
'class="p-1.5 text-muted-foreground hover:text-primary hover:bg-primary/10 rounded transition-colors" ' +
|
||||||
|
'title="' + t('common.refresh') + '">' +
|
||||||
|
'<i data-lucide="refresh-cw" class="w-4 h-4' + (isLoading ? ' animate-spin' : '') + '"></i>' +
|
||||||
|
'</button>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'<p>' + t('hook.wizard.autoDetectionInfo') + '</p>' +
|
'<p>' + t('hook.wizard.autoDetectionInfo') + '</p>' +
|
||||||
'<p class="mt-2">' + t('hook.wizard.availableSkills') + ' ' + skillBadges + '</p>' +
|
'<div class="mt-2 flex items-center gap-2 flex-wrap">' +
|
||||||
|
'<span>' + t('hook.wizard.availableSkills') + '</span>' +
|
||||||
|
skillBadges +
|
||||||
|
'</div>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -926,27 +937,59 @@ function renderSkillContextConfig() {
|
|||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
var noSkillsWarning = '';
|
var isLoading = typeof window.availableSkills === 'undefined' || window.skillsLoading;
|
||||||
if (availableSkills.length === 0) {
|
var skillsStatusHtml = '';
|
||||||
noSkillsWarning = '<div class="text-xs text-amber-500 flex items-center gap-1">' +
|
if (isLoading) {
|
||||||
|
skillsStatusHtml = '<span class="text-xs text-muted-foreground flex items-center gap-1">' +
|
||||||
|
'<i data-lucide="loader-2" class="w-3 h-3 animate-spin"></i>' +
|
||||||
|
t('common.loading') +
|
||||||
|
'</span>';
|
||||||
|
} else if (availableSkills.length === 0) {
|
||||||
|
skillsStatusHtml = '<span class="text-xs text-amber-500 flex items-center gap-1">' +
|
||||||
'<i data-lucide="alert-triangle" class="w-3 h-3"></i>' +
|
'<i data-lucide="alert-triangle" class="w-3 h-3"></i>' +
|
||||||
t('hook.wizard.noSkillsFound') +
|
t('hook.wizard.noSkillsFound') +
|
||||||
'</div>';
|
'</span>';
|
||||||
|
} else {
|
||||||
|
skillsStatusHtml = '<span class="text-xs text-muted-foreground">' +
|
||||||
|
availableSkills.length + ' ' + t('skills.skillsCount') +
|
||||||
|
'</span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '<div class="space-y-4">' +
|
return '<div class="space-y-4">' +
|
||||||
'<div class="flex items-center justify-between">' +
|
'<div class="flex items-center justify-between">' +
|
||||||
'<span class="text-sm font-medium text-foreground">' + t('hook.wizard.configureSkills') + '</span>' +
|
'<div class="flex items-center gap-2">' +
|
||||||
|
'<span class="text-sm font-medium text-foreground">' + t('hook.wizard.configureSkills') + '</span>' +
|
||||||
|
skillsStatusHtml +
|
||||||
|
'<button type="button" onclick="refreshAvailableSkills()" ' +
|
||||||
|
'class="p-1 text-muted-foreground hover:text-primary hover:bg-primary/10 rounded transition-colors" ' +
|
||||||
|
'title="' + t('common.refresh') + '">' +
|
||||||
|
'<i data-lucide="refresh-cw" class="w-3 h-3' + (isLoading ? ' animate-spin' : '') + '"></i>' +
|
||||||
|
'</button>' +
|
||||||
|
'</div>' +
|
||||||
'<button type="button" onclick="addSkillConfig()" ' +
|
'<button type="button" onclick="addSkillConfig()" ' +
|
||||||
'class="px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded-lg hover:opacity-90 flex items-center gap-1">' +
|
'class="px-3 py-1.5 text-xs bg-primary text-primary-foreground rounded-lg hover:opacity-90 flex items-center gap-1">' +
|
||||||
'<i data-lucide="plus" class="w-3 h-3"></i> ' + t('hook.wizard.addSkill') +
|
'<i data-lucide="plus" class="w-3 h-3"></i> ' + t('hook.wizard.addSkill') +
|
||||||
'</button>' +
|
'</button>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'<div id="skillConfigsList" class="space-y-3">' + configListHtml + '</div>' +
|
'<div id="skillConfigsList" class="space-y-3">' + configListHtml + '</div>' +
|
||||||
noSkillsWarning +
|
|
||||||
'</div>';
|
'</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function refreshAvailableSkills() {
|
||||||
|
// Set loading state
|
||||||
|
window.skillsLoading = true;
|
||||||
|
renderWizardModalContent();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await loadAvailableSkills();
|
||||||
|
} finally {
|
||||||
|
window.skillsLoading = false;
|
||||||
|
renderWizardModalContent();
|
||||||
|
// Refresh Lucide icons
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function addSkillConfig() {
|
function addSkillConfig() {
|
||||||
if (!wizardConfig.skillConfigs) {
|
if (!wizardConfig.skillConfigs) {
|
||||||
wizardConfig.skillConfigs = [];
|
wizardConfig.skillConfigs = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user