mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-18 18:48:48 +08:00
feat: add MCP server for semantic code search with FastMCP integration
This commit is contained in:
92
.claude/skills/wf-player/phases/01-load.md
Normal file
92
.claude/skills/wf-player/phases/01-load.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Phase 1: Load & Bind
|
||||
|
||||
## Objective
|
||||
|
||||
Locate and load the workflow template, collect any missing context variables from the user, bind all `{variable}` references.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1.1 — Resolve Template Path
|
||||
|
||||
Parse `$ARGUMENTS` for template identifier:
|
||||
|
||||
**Path resolution order**:
|
||||
1. Absolute path: use as-is
|
||||
2. Relative path (starts with `.`): resolve from cwd
|
||||
3. Slug only (e.g. `feature-tdd-review`): look up in `.workflow/templates/index.json` → get path
|
||||
4. Partial slug match: scan index for closest match → confirm with user
|
||||
|
||||
If not found:
|
||||
- Show available templates from index
|
||||
- AskUserQuestion: "Which template to run?"
|
||||
|
||||
### Step 1.2 — Parse --context Arguments
|
||||
|
||||
Extract `--context key=value` pairs from `$ARGUMENTS`.
|
||||
|
||||
Examples:
|
||||
```
|
||||
--context goal="Implement user auth" --context scope="src/auth"
|
||||
--context goal='Fix login bug' scope=src/auth
|
||||
```
|
||||
|
||||
Build `bound_context = { goal: "...", scope: "..." }`.
|
||||
|
||||
### Step 1.3 — Load Template
|
||||
|
||||
Read template JSON from resolved path.
|
||||
|
||||
Validate:
|
||||
- `template_id`, `nodes`, `edges`, `context_schema` all present
|
||||
- `nodes` array non-empty
|
||||
|
||||
### Step 1.4 — Collect Missing Required Variables
|
||||
|
||||
For each variable in `context_schema` where `required: true`:
|
||||
- If not in `bound_context`: collect via AskUserQuestion
|
||||
- If has `default` value: use default if not provided
|
||||
|
||||
```
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "Provide values for required workflow inputs:",
|
||||
header: "Workflow: <template.name>",
|
||||
// one question per missing required variable
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
For optional variables not provided: use `default` value or leave as empty string.
|
||||
|
||||
### Step 1.5 — Bind Variables
|
||||
|
||||
Apply substitution throughout all `args_template` strings:
|
||||
- Replace `{variable_name}` with `bound_context[variable_name]`
|
||||
- Leave `{N-001.session_id}` and `{prev_*}` references unresolved — these are runtime-resolved in Phase 3
|
||||
|
||||
Write bound context to memory for Phase 3 use.
|
||||
|
||||
### Step 1.6 — Dry Run Output (if --dry-run)
|
||||
|
||||
Print execution plan and exit:
|
||||
```
|
||||
Workflow: <template.name>
|
||||
Context:
|
||||
goal = "<value>"
|
||||
scope = "<value>"
|
||||
|
||||
Execution Plan:
|
||||
[1] N-001 [skill] workflow-lite-plan "<goal>"
|
||||
[2] CP-01 [checkpoint] After Plan auto-continue
|
||||
[3] N-002 [skill] workflow-execute --resume-session {N-001.session_id}
|
||||
[4] CP-02 [checkpoint] Before Tests pause-for-user
|
||||
[5] N-003 [skill] workflow-test-fix --session {N-002.session_id}
|
||||
|
||||
To execute: Skill(skill="wf-player", args="<slug> --context goal='...'")
|
||||
```
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- Template loaded and validated
|
||||
- All required context variables bound
|
||||
- bound_context{} available for Phase 2
|
||||
110
.claude/skills/wf-player/phases/02-instantiate.md
Normal file
110
.claude/skills/wf-player/phases/02-instantiate.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Phase 2: Instantiate — Init Session State
|
||||
|
||||
## Objective
|
||||
|
||||
Create the WFR session directory, initialize `session-state.json` with all nodes marked pending, compute topological execution order.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 2.1 — Generate Session ID
|
||||
|
||||
```
|
||||
session_id = "WFR-<template-slug>-<YYYYMMDD>-<HHmmss>"
|
||||
session_dir = ".workflow/sessions/<session_id>/"
|
||||
```
|
||||
|
||||
Create session directory.
|
||||
|
||||
### Step 2.2 — Topological Sort
|
||||
|
||||
Run topological sort on `template.nodes` + `template.edges`:
|
||||
|
||||
```
|
||||
function topoSort(nodes, edges):
|
||||
build adjacency list from edges
|
||||
Kahn's algorithm (BFS from nodes with no incoming edges)
|
||||
return ordered node IDs
|
||||
```
|
||||
|
||||
**Parallel group handling**: Nodes in the same `parallel_group` can execute concurrently. In topological order, keep them adjacent and mark them as a parallel batch.
|
||||
|
||||
Store `execution_plan`:
|
||||
```json
|
||||
[
|
||||
{ "batch": 1, "nodes": ["N-001"], "parallel": false },
|
||||
{ "batch": 2, "nodes": ["CP-01"], "parallel": false },
|
||||
{ "batch": 3, "nodes": ["N-002a", "N-002b"], "parallel": true },
|
||||
{ "batch": 4, "nodes": ["N-003"], "parallel": false }
|
||||
]
|
||||
```
|
||||
|
||||
### Step 2.3 — Init Node States
|
||||
|
||||
For each node in template:
|
||||
```json
|
||||
{
|
||||
"N-001": {
|
||||
"status": "pending",
|
||||
"started_at": null,
|
||||
"completed_at": null,
|
||||
"session_id": null,
|
||||
"output_path": null,
|
||||
"artifacts": [],
|
||||
"error": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Checkpoint nodes:
|
||||
```json
|
||||
{
|
||||
"CP-01": {
|
||||
"status": "pending",
|
||||
"saved_at": null,
|
||||
"snapshot_path": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2.4 — Write session-state.json
|
||||
|
||||
See `specs/state-schema.md` for full schema. Write to `<session_dir>/session-state.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "<WFR-id>",
|
||||
"template_id": "<template.template_id>",
|
||||
"template_path": "<path to template>",
|
||||
"template_name": "<template.name>",
|
||||
"status": "running",
|
||||
"context": { /* bound_context from Phase 1 */ },
|
||||
"execution_plan": [ /* batches */ ],
|
||||
"current_batch": 1,
|
||||
"current_node": "N-001",
|
||||
"last_checkpoint": null,
|
||||
"node_states": { /* all nodes as pending */ },
|
||||
"created_at": "<ISO>",
|
||||
"updated_at": "<ISO>"
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2.5 — Show Execution Start Banner
|
||||
|
||||
```
|
||||
[wf-player] ============================================
|
||||
[wf-player] Starting: <template.name>
|
||||
[wf-player] Session: <session_id>
|
||||
[wf-player] Context: goal="<value>"
|
||||
[wf-player]
|
||||
[wf-player] Plan: <N> nodes, <C> checkpoints
|
||||
[wf-player] N-001 [skill] workflow-lite-plan
|
||||
[wf-player] CP-01 [checkpoint] After Plan
|
||||
[wf-player] N-002 [skill] workflow-execute
|
||||
[wf-player] ============================================
|
||||
```
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- `<session_dir>/session-state.json` written
|
||||
- `execution_plan` has valid topological order
|
||||
- Status = "running"
|
||||
211
.claude/skills/wf-player/phases/03-execute.md
Normal file
211
.claude/skills/wf-player/phases/03-execute.md
Normal file
@@ -0,0 +1,211 @@
|
||||
# Phase 3: Execute Loop
|
||||
|
||||
## Objective
|
||||
|
||||
Execute each node batch in topological order. Use the correct mechanism per node type. Save state after every checkpoint. Support CLI serial-blocking with hook callback resume.
|
||||
|
||||
## Pre-execution: Runtime Reference Resolution
|
||||
|
||||
Before executing each node, resolve any `{N-xxx.field}` and `{prev_*}` references in `args_template`:
|
||||
|
||||
```
|
||||
function resolveArgs(args_template, node_id, session_state):
|
||||
for each {ref} in args_template:
|
||||
if ref matches {variable}:
|
||||
replace with session_state.context[variable]
|
||||
if ref matches {N-001.session_id}:
|
||||
replace with session_state.node_states["N-001"].session_id
|
||||
if ref matches {N-001.output_path}:
|
||||
replace with session_state.node_states["N-001"].output_path
|
||||
if ref matches {prev_session_id}:
|
||||
find previous non-checkpoint node -> replace with its session_id
|
||||
if ref matches {prev_output}:
|
||||
find previous non-checkpoint node -> replace with its output_text
|
||||
if ref matches {prev_output_path}:
|
||||
find previous non-checkpoint node -> replace with its output_path
|
||||
return resolved_args
|
||||
```
|
||||
|
||||
## Node Execution by Type
|
||||
|
||||
Read `specs/node-executor.md` for full details. Summary:
|
||||
|
||||
### skill node
|
||||
|
||||
```
|
||||
resolved_args = resolveArgs(node.args_template, ...)
|
||||
mark node status = "running", write session-state.json
|
||||
|
||||
result = Skill(skill=node.executor, args=resolved_args)
|
||||
|
||||
extract from result: session_id, output_path, artifacts[]
|
||||
update node_states[node.id]:
|
||||
status = "completed"
|
||||
session_id = extracted_session_id
|
||||
output_path = extracted_output_path
|
||||
artifacts = extracted_artifacts
|
||||
completed_at = now()
|
||||
|
||||
write session-state.json
|
||||
advance to next node
|
||||
```
|
||||
|
||||
### command node
|
||||
|
||||
Same as skill node but executor is a namespaced command:
|
||||
```
|
||||
Skill(skill=node.executor, args=resolved_args)
|
||||
```
|
||||
|
||||
### cli node — CRITICAL: serial blocking
|
||||
|
||||
```
|
||||
resolved_args = resolveArgs(node.args_template, ...)
|
||||
mark node status = "running", write session-state.json
|
||||
|
||||
Bash({
|
||||
command: `ccw cli -p "${resolved_args}" --tool ${node.cli_tool} --mode ${node.cli_mode} --rule ${node.cli_rule}`,
|
||||
run_in_background: true
|
||||
})
|
||||
|
||||
write session-state.json // persist "running" state
|
||||
STOP — wait for hook callback
|
||||
```
|
||||
|
||||
**Hook callback resumes here**:
|
||||
```
|
||||
// Called when ccw cli completes
|
||||
load session-state.json
|
||||
find node with status "running"
|
||||
extract result: exec_id, output_path, cli_output
|
||||
update node_states[node.id]:
|
||||
status = "completed"
|
||||
output_path = extracted_output_path
|
||||
completed_at = now()
|
||||
write session-state.json
|
||||
advance to next node
|
||||
```
|
||||
|
||||
### agent node
|
||||
|
||||
```
|
||||
resolved_args = resolveArgs(node.args_template, ...)
|
||||
mark node status = "running", write session-state.json
|
||||
|
||||
result = Agent({
|
||||
subagent_type: node.executor,
|
||||
prompt: resolved_args,
|
||||
run_in_background: node.run_in_background ?? false,
|
||||
description: node.name
|
||||
})
|
||||
|
||||
update node_states[node.id]:
|
||||
status = "completed"
|
||||
output_path = result.output_path or session_dir + "/artifacts/" + node.id + ".md"
|
||||
completed_at = now()
|
||||
write session-state.json
|
||||
advance to next node
|
||||
```
|
||||
|
||||
**Parallel agent nodes** (same parallel_group):
|
||||
```
|
||||
// Launch all agents in parallel
|
||||
for each node in parallel_batch:
|
||||
mark node status = "running"
|
||||
|
||||
Agent({
|
||||
subagent_type: node.executor,
|
||||
prompt: resolveArgs(node.args_template, ...),
|
||||
run_in_background: true, // parallel
|
||||
description: node.name
|
||||
})
|
||||
|
||||
// Wait for all to complete (Agent with run_in_background=false blocks — use team-coordinate pattern)
|
||||
// team-coordinate: spawn as team-workers with callbacks if complex
|
||||
// For simple parallel: use multiple Agent calls synchronously or use team-coordinate's spawn-and-stop
|
||||
```
|
||||
|
||||
### checkpoint node
|
||||
|
||||
```
|
||||
// Save snapshot
|
||||
snapshot = {
|
||||
session_id: session_state.session_id,
|
||||
checkpoint_id: node.id,
|
||||
checkpoint_name: node.name,
|
||||
saved_at: now(),
|
||||
node_states_snapshot: session_state.node_states,
|
||||
last_completed_node: previous_node_id
|
||||
}
|
||||
|
||||
write session-state.json (last_checkpoint = node.id)
|
||||
write <session_dir>/checkpoints/<node.id>.json
|
||||
|
||||
if node.auto_continue == false:
|
||||
// Pause for user
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: node.description + "\n\nReview checkpoint state and confirm to continue.",
|
||||
header: "Checkpoint: " + node.name,
|
||||
options: [
|
||||
{ label: "Continue", description: "Proceed to next node" },
|
||||
{ label: "Pause", description: "Save state and exit (resume later)" },
|
||||
{ label: "Abort", description: "Stop execution" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
|
||||
on "Pause":
|
||||
session_state.status = "paused"
|
||||
write session-state.json
|
||||
output "Session paused. Resume with: Skill(skill='wf-player', args='--resume <session_id>')"
|
||||
EXIT
|
||||
|
||||
on "Abort":
|
||||
session_state.status = "aborted"
|
||||
write session-state.json
|
||||
EXIT
|
||||
|
||||
// auto_continue or user chose Continue
|
||||
mark checkpoint status = "completed"
|
||||
write session-state.json
|
||||
advance to next node
|
||||
```
|
||||
|
||||
## Progress Display
|
||||
|
||||
After each node completes, print progress:
|
||||
```
|
||||
[wf-player] [2/5] CP-01 checkpoint saved ✓
|
||||
[wf-player] [3/5] N-002 workflow-execute ... running
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
On node failure (exception or skill returning error state):
|
||||
|
||||
```
|
||||
on_fail = node.on_fail || "abort"
|
||||
|
||||
if on_fail == "skip":
|
||||
mark node status = "skipped"
|
||||
log warning
|
||||
advance to next node
|
||||
|
||||
if on_fail == "retry":
|
||||
retry once
|
||||
if still fails: fall through to abort
|
||||
|
||||
if on_fail == "abort":
|
||||
AskUserQuestion:
|
||||
- Retry
|
||||
- Skip this node
|
||||
- Abort workflow
|
||||
handle choice accordingly
|
||||
```
|
||||
|
||||
## Loop Termination
|
||||
|
||||
After last node in execution_plan completes:
|
||||
- All node_states should be "completed" or "skipped"
|
||||
- Proceed to Phase 4 (Complete)
|
||||
93
.claude/skills/wf-player/phases/04-complete.md
Normal file
93
.claude/skills/wf-player/phases/04-complete.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Phase 4: Complete — Archive + Summary
|
||||
|
||||
## Objective
|
||||
|
||||
Mark session complete, output execution summary with artifact paths, offer archive/keep options.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 4.1 — Mark Session Complete
|
||||
|
||||
Load `session-state.json`.
|
||||
|
||||
Set:
|
||||
```json
|
||||
{
|
||||
"status": "completed",
|
||||
"completed_at": "<ISO timestamp>"
|
||||
}
|
||||
```
|
||||
|
||||
Write back to `session-state.json`.
|
||||
|
||||
### Step 4.2 — Collect Artifacts
|
||||
|
||||
Aggregate all artifacts from node_states:
|
||||
```javascript
|
||||
const artifacts = Object.values(node_states)
|
||||
.filter(s => s.artifacts && s.artifacts.length > 0)
|
||||
.flatMap(s => s.artifacts.map(a => ({ node: s.node_id, path: a })));
|
||||
|
||||
const outputPaths = Object.values(node_states)
|
||||
.filter(s => s.output_path)
|
||||
.map(s => ({ node: s.node_id, path: s.output_path }));
|
||||
```
|
||||
|
||||
### Step 4.3 — Execution Summary
|
||||
|
||||
```
|
||||
[wf-player] ============================================
|
||||
[wf-player] COMPLETE: <template_name>
|
||||
[wf-player]
|
||||
[wf-player] Session: <session_id>
|
||||
[wf-player] Context: goal="<value>"
|
||||
[wf-player]
|
||||
[wf-player] Nodes: <completed>/<total> completed
|
||||
[wf-player] N-001 workflow-lite-plan ✓ (WFS-plan-xxx)
|
||||
[wf-player] CP-01 After Plan ✓ (checkpoint saved)
|
||||
[wf-player] N-002 workflow-execute ✓ (WFS-exec-xxx)
|
||||
[wf-player] CP-02 Before Tests ✓ (checkpoint saved)
|
||||
[wf-player] N-003 workflow-test-fix ✓ (WFS-test-xxx)
|
||||
[wf-player]
|
||||
[wf-player] Artifacts:
|
||||
[wf-player] - IMPL_PLAN.md (N-001)
|
||||
[wf-player] - src/auth/index.ts (N-002)
|
||||
[wf-player] - test/auth.test.ts (N-003)
|
||||
[wf-player]
|
||||
[wf-player] Session dir: .workflow/sessions/<session_id>/
|
||||
[wf-player] ============================================
|
||||
```
|
||||
|
||||
### Step 4.4 — Completion Action
|
||||
|
||||
```
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "Workflow complete. What would you like to do?",
|
||||
header: "Completion Action",
|
||||
options: [
|
||||
{ label: "Archive session", description: "Move session to .workflow/sessions/archive/" },
|
||||
{ label: "Keep session", description: "Leave session active for follow-up" },
|
||||
{ label: "Run again", description: "Re-run template with same or new context" },
|
||||
{ label: "Nothing", description: "Done" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
**Archive**:
|
||||
- Move `<session_dir>` to `.workflow/sessions/archive/<session_id>/`
|
||||
- Update `session-state.json` status = "archived"
|
||||
|
||||
**Keep**: No action, session stays at `.workflow/sessions/<session_id>/`
|
||||
|
||||
**Run again**:
|
||||
- AskUserQuestion: "Same context or new?" → new context → re-enter Phase 1
|
||||
|
||||
**Nothing**: Output final artifact paths list, done.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- session-state.json status = "completed" or "archived"
|
||||
- All artifact paths listed in console output
|
||||
- User presented completion action options
|
||||
Reference in New Issue
Block a user