@curdx/flow 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +25 -2
- package/.claude-plugin/plugin.json +10 -1
- package/CHANGELOG.md +32 -0
- package/README.md +3 -0
- package/README.zh.md +3 -0
- package/agent-preamble/preamble.md +2 -2
- package/agents/flow-qa-engineer.md +16 -15
- package/agents/flow-ui-researcher.md +2 -2
- package/agents/flow-verifier.md +3 -3
- package/bin/curdx-flow +5 -0
- package/cli/README.md +10 -9
- package/cli/install-companions.js +4 -1
- package/cli/install-required-plugins.js +18 -5
- package/cli/install-self-update.js +2 -91
- package/cli/install.js +12 -1
- package/cli/lib/claude.js +42 -11
- package/cli/lib/doctor-report.js +47 -8
- package/cli/lib/semver.js +95 -0
- package/cli/utils.js +1 -0
- package/hooks/scripts/quick-mode-guard.sh +6 -7
- package/knowledge/execution-strategies.md +5 -5
- package/knowledge/planning-reviews.md +2 -2
- package/knowledge/wave-execution.md +17 -17
- package/package.json +3 -2
- package/schemas/agent-frontmatter.schema.json +66 -0
- package/schemas/config.schema.json +23 -3
- package/schemas/gate-frontmatter.schema.json +30 -0
- package/schemas/hooks.schema.json +83 -0
- package/schemas/plugin-manifest.schema.json +66 -0
- package/schemas/skill-frontmatter.schema.json +72 -0
- package/schemas/spec-state.schema.json +6 -1
- package/skills/brownfield-index/SKILL.md +2 -1
- package/skills/browser-qa/SKILL.md +5 -4
- package/skills/debug/SKILL.md +1 -0
- package/skills/epic/SKILL.md +2 -1
- package/skills/fast/SKILL.md +2 -1
- package/skills/help/SKILL.md +1 -0
- package/skills/implement/SKILL.md +6 -5
- package/skills/implement/references/wave-execution.md +9 -9
- package/skills/init/SKILL.md +1 -0
- package/skills/review/SKILL.md +2 -1
- package/skills/security-audit/SKILL.md +2 -1
- package/skills/spec/SKILL.md +4 -3
- package/skills/start/SKILL.md +2 -1
- package/skills/ui-sketch/SKILL.md +2 -1
- package/skills/verify/SKILL.md +2 -1
- package/templates/CONTEXT.md.tmpl +1 -1
- package/templates/PROJECT.md.tmpl +1 -1
- package/templates/config.json.tmpl +6 -6
- package/templates/progress.md.tmpl +2 -2
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
function normalizeVersionToken(token) {
|
|
2
|
+
return /^\d+$/.test(token) ? Number(token) : token;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function parseVersion(version) {
|
|
6
|
+
const normalized = String(version || "").trim().replace(/^v/i, "");
|
|
7
|
+
const [coreRaw = "0", prereleaseRaw] = normalized.split("-", 2);
|
|
8
|
+
const core = coreRaw.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
9
|
+
const prerelease = prereleaseRaw
|
|
10
|
+
? prereleaseRaw.split(/[.-]/).filter(Boolean).map(normalizeVersionToken)
|
|
11
|
+
: [];
|
|
12
|
+
|
|
13
|
+
return { core, prerelease };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function compareIdentifier(left, right) {
|
|
17
|
+
if (left === right) {
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const leftIsNumber = typeof left === "number";
|
|
22
|
+
const rightIsNumber = typeof right === "number";
|
|
23
|
+
|
|
24
|
+
if (leftIsNumber && rightIsNumber) {
|
|
25
|
+
return left > right ? 1 : -1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (leftIsNumber) {
|
|
29
|
+
return -1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (rightIsNumber) {
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return left > right ? 1 : -1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function compareVersions(leftVersion, rightVersion) {
|
|
40
|
+
const left = parseVersion(leftVersion);
|
|
41
|
+
const right = parseVersion(rightVersion);
|
|
42
|
+
const coreLength = Math.max(left.core.length, right.core.length);
|
|
43
|
+
|
|
44
|
+
for (let index = 0; index < coreLength; index += 1) {
|
|
45
|
+
const leftPart = left.core[index] ?? 0;
|
|
46
|
+
const rightPart = right.core[index] ?? 0;
|
|
47
|
+
if (leftPart !== rightPart) {
|
|
48
|
+
return leftPart > rightPart ? 1 : -1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const leftHasPrerelease = left.prerelease.length > 0;
|
|
53
|
+
const rightHasPrerelease = right.prerelease.length > 0;
|
|
54
|
+
|
|
55
|
+
if (!leftHasPrerelease && !rightHasPrerelease) {
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!leftHasPrerelease) {
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!rightHasPrerelease) {
|
|
64
|
+
return -1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const prereleaseLength = Math.max(left.prerelease.length, right.prerelease.length);
|
|
68
|
+
for (let index = 0; index < prereleaseLength; index += 1) {
|
|
69
|
+
const leftPart = left.prerelease[index];
|
|
70
|
+
const rightPart = right.prerelease[index];
|
|
71
|
+
|
|
72
|
+
if (leftPart === undefined) {
|
|
73
|
+
return -1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (rightPart === undefined) {
|
|
77
|
+
return 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const comparison = compareIdentifier(leftPart, rightPart);
|
|
81
|
+
if (comparison !== 0) {
|
|
82
|
+
return comparison;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function isVersionNewer(latestVersion, currentVersion) {
|
|
90
|
+
return compareVersions(latestVersion, currentVersion) > 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function isVersionAtLeast(version, minimumVersion) {
|
|
94
|
+
return compareVersions(version, minimumVersion) >= 0;
|
|
95
|
+
}
|
package/cli/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# CurDX-Flow PreToolUse Hook for AskUserQuestion
|
|
3
|
-
# Blocks AskUserQuestion when the active spec has quickMode=true
|
|
4
|
-
# This prevents
|
|
3
|
+
# Blocks AskUserQuestion when the active spec has quickMode=true.
|
|
4
|
+
# This prevents quick execution loops from stalling waiting for user input.
|
|
5
5
|
#
|
|
6
6
|
# The hook reads Claude's PreToolUse input JSON from stdin. We only act when
|
|
7
7
|
# the tool being invoked is AskUserQuestion.
|
|
@@ -34,7 +34,7 @@ if [ "$TOOL_NAME" != "AskUserQuestion" ]; then
|
|
|
34
34
|
exit 0
|
|
35
35
|
fi
|
|
36
36
|
|
|
37
|
-
# Check if we're in a flow project with quick mode enabled
|
|
37
|
+
# Check if we're in a flow project with quick mode enabled.
|
|
38
38
|
[ ! -d ".flow" ] && exit 0
|
|
39
39
|
|
|
40
40
|
ACTIVE=$(cat .flow/.active-spec 2>/dev/null)
|
|
@@ -43,7 +43,7 @@ ACTIVE=$(cat .flow/.active-spec 2>/dev/null)
|
|
|
43
43
|
STATE_FILE=".flow/specs/$ACTIVE/.state.json"
|
|
44
44
|
[ ! -f "$STATE_FILE" ] && exit 0
|
|
45
45
|
|
|
46
|
-
# Read quickMode
|
|
46
|
+
# Read quickMode. Pass STATE_FILE via env (NOT shell interpolation
|
|
47
47
|
# into the python source) so an active-spec name containing quotes/$ cannot
|
|
48
48
|
# inject python code.
|
|
49
49
|
export STATE_FILE
|
|
@@ -52,15 +52,14 @@ import json, os
|
|
|
52
52
|
try:
|
|
53
53
|
s = json.load(open(os.environ["STATE_FILE"]))
|
|
54
54
|
qm = s.get("quickMode", False)
|
|
55
|
-
|
|
56
|
-
print("true" if (qm or mode == "autonomous") else "false")
|
|
55
|
+
print("true" if qm else "false")
|
|
57
56
|
except Exception:
|
|
58
57
|
print("false")
|
|
59
58
|
' 2>/dev/null)
|
|
60
59
|
|
|
61
60
|
if [ "$QUICK_MODE" = "true" ]; then
|
|
62
61
|
# Block and inject guidance
|
|
63
|
-
MSG="[CurDX-Flow quick-mode-guard] Active spec '$ACTIVE' is in quick mode
|
|
62
|
+
MSG="[CurDX-Flow quick-mode-guard] Active spec '$ACTIVE' is in quick mode — AskUserQuestion is forbidden. Decide based on user preferences in .flow/CONTEXT.md plus the most reasonable assumption, and record your assumption in .progress.md."
|
|
64
63
|
emit_pretooluse_deny "$MSG"
|
|
65
64
|
exit 0
|
|
66
65
|
fi
|
|
@@ -65,8 +65,8 @@ The main agent reads tasks.md and **dispatches a fresh flow-executor subagent pe
|
|
|
65
65
|
|
|
66
66
|
```
|
|
67
67
|
main agent
|
|
68
|
-
├─
|
|
69
|
-
├─
|
|
68
|
+
├─ Agent() → subagent(task 1) → TASK_COMPLETE
|
|
69
|
+
├─ Agent() → subagent(task 2) → TASK_COMPLETE
|
|
70
70
|
└─ ...
|
|
71
71
|
(each subagent has isolated context)
|
|
72
72
|
```
|
|
@@ -86,7 +86,7 @@ main agent
|
|
|
86
86
|
### Disadvantages
|
|
87
87
|
- Dispatch overhead (each subagent reloads context)
|
|
88
88
|
- Cross-task info sharing goes through files (.progress.md)
|
|
89
|
-
- Not parallel (a single
|
|
89
|
+
- Not parallel (a single Agent call is serial)
|
|
90
90
|
|
|
91
91
|
### Enable
|
|
92
92
|
```bash
|
|
@@ -149,7 +149,7 @@ main agent → task N → Stop → stop-watcher.sh
|
|
|
149
149
|
### How it works
|
|
150
150
|
- Tasks in tasks.md marked `[P]` are "parallel-safe"
|
|
151
151
|
- The main agent identifies consecutive `[P]` tasks as one wave
|
|
152
|
-
- **In a single message**, it dispatches multiple
|
|
152
|
+
- **In a single message**, it dispatches multiple Agent() tool calls simultaneously
|
|
153
153
|
- All tasks within a wave run in parallel
|
|
154
154
|
- Waves run serially relative to each other
|
|
155
155
|
|
|
@@ -175,7 +175,7 @@ main agent
|
|
|
175
175
|
### Disadvantages
|
|
176
176
|
- Parallel conflict risk (e.g., two tasks editing the same file)
|
|
177
177
|
- Requires flow-planner to have tagged `[P]` (Phase 1-generated tasks.md should)
|
|
178
|
-
- Main agent manages many
|
|
178
|
+
- Main agent manages many Agent calls, high context pressure
|
|
179
179
|
|
|
180
180
|
### Enable
|
|
181
181
|
```bash
|
|
@@ -149,7 +149,7 @@ Essentially runs `flow-architect` again — but this time not to generate the de
|
|
|
149
149
|
|
|
150
150
|
A new agent `flow-devex-reviewer`, or reuse `flow-reviewer` by passing in the devex-gate.
|
|
151
151
|
|
|
152
|
-
Phase 5 implementation: reuse `flow-reviewer` +
|
|
152
|
+
Phase 5 implementation: reuse `flow-reviewer` + `@${CLAUDE_PLUGIN_ROOT}/gates/devex-gate.md`.
|
|
153
153
|
|
|
154
154
|
---
|
|
155
155
|
|
|
@@ -160,7 +160,7 @@ Phase 5 implementation: reuse `flow-reviewer` + `@gates/devex-gate.md`.
|
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
Workflow:
|
|
163
|
-
1. In parallel (single-message multi-
|
|
163
|
+
1. In parallel (single-message multi-Agent), dispatch 4 reviews
|
|
164
164
|
2. Wait for all to return
|
|
165
165
|
3. Merge findings, sort by priority
|
|
166
166
|
4. Present to the user for decision
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Wave Execution — DAG Parallel Execution Strategy
|
|
2
2
|
|
|
3
|
-
> One of Phase 2's 4 execution strategies. Identify parallel-safe task groups via `[P]` markers, dispatch multiple
|
|
3
|
+
> One of Phase 2's 4 execution strategies. Identify parallel-safe task groups via `[P]` markers, dispatch multiple Agent tool calls **in a single message**, run in parallel within a wave and serially across waves.
|
|
4
4
|
>
|
|
5
5
|
> Agents reference this via `@${CLAUDE_PLUGIN_ROOT}/knowledge/wave-execution.md`.
|
|
6
6
|
|
|
@@ -23,10 +23,10 @@ tasks.md:
|
|
|
23
23
|
1.7 [P] add README to user
|
|
24
24
|
|
|
25
25
|
Analysis:
|
|
26
|
-
Wave 1: { 1.1, 1.2, 1.3 } — parallel (3
|
|
26
|
+
Wave 1: { 1.1, 1.2, 1.3 } — parallel (3 Agent calls)
|
|
27
27
|
Wave 2: { 1.4 } — serial (VERIFY breaks)
|
|
28
28
|
Wave 3: { 1.5 } — serial (no [P])
|
|
29
|
-
Wave 4: { 1.6, 1.7 } — parallel (2
|
|
29
|
+
Wave 4: { 1.6, 1.7 } — parallel (2 Agent calls)
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
---
|
|
@@ -95,11 +95,11 @@ Rules:
|
|
|
95
95
|
|
|
96
96
|
---
|
|
97
97
|
|
|
98
|
-
## How Parallel
|
|
98
|
+
## How Parallel Agent Dispatch Actually Works
|
|
99
99
|
|
|
100
|
-
### Key: multiple
|
|
100
|
+
### Key: multiple Agent tool calls in a single message
|
|
101
101
|
|
|
102
|
-
Claude Code's
|
|
102
|
+
Claude Code's Agent tool runs in parallel **when multiple calls appear in the same message**.
|
|
103
103
|
Across **separate messages** → runs sequentially.
|
|
104
104
|
|
|
105
105
|
### Correct form (Wave strategy)
|
|
@@ -107,9 +107,9 @@ Across **separate messages** → runs sequentially.
|
|
|
107
107
|
```
|
|
108
108
|
# In a single main-agent response:
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
Agent(description="Task 1.1", prompt="...execute 1.1...")
|
|
111
|
+
Agent(description="Task 1.2", prompt="...execute 1.2...")
|
|
112
|
+
Agent(description="Task 1.3", prompt="...execute 1.3...")
|
|
113
113
|
|
|
114
114
|
# Wait for all to return before continuing to the next wave
|
|
115
115
|
```
|
|
@@ -118,13 +118,13 @@ Task(description="Task 1.3", prompt="...execute 1.3...")
|
|
|
118
118
|
|
|
119
119
|
```
|
|
120
120
|
# One response:
|
|
121
|
-
|
|
121
|
+
Agent(description="Task 1.1", ...)
|
|
122
122
|
# wait for return
|
|
123
123
|
# Next response:
|
|
124
|
-
|
|
124
|
+
Agent(description="Task 1.2", ...)
|
|
125
125
|
# wait for return
|
|
126
126
|
# Next response:
|
|
127
|
-
|
|
127
|
+
Agent(description="Task 1.3", ...)
|
|
128
128
|
```
|
|
129
129
|
|
|
130
130
|
This is not parallel — it's serial subagent. The Wave strategy loses its meaning.
|
|
@@ -142,9 +142,9 @@ for wave_index, wave in enumerate(waves):
|
|
|
142
142
|
echo " • $task.id $task.title"
|
|
143
143
|
|
|
144
144
|
# === Step 2: dispatch (key: within a single message) ===
|
|
145
|
-
# This is the main agent's response body. Call N
|
|
145
|
+
# This is the main agent's response body. Call N Agent tools at once.
|
|
146
146
|
results = await asyncio.gather([
|
|
147
|
-
|
|
147
|
+
Agent(
|
|
148
148
|
description=f"execute {task.id}",
|
|
149
149
|
prompt=f"""
|
|
150
150
|
You are the flow-executor agent.
|
|
@@ -193,7 +193,7 @@ for wave_index, wave in enumerate(waves):
|
|
|
193
193
|
return
|
|
194
194
|
|
|
195
195
|
# === Step 6: inter-wave synchronization point ===
|
|
196
|
-
# All
|
|
196
|
+
# All Agent calls complete = wave ends
|
|
197
197
|
# Before next wave starts, confirm git state is consistent
|
|
198
198
|
|
|
199
199
|
# All waves done
|
|
@@ -320,7 +320,7 @@ Progress: Wave 2/5 (60%)
|
|
|
320
320
|
|
|
321
321
|
### Ctrl+C interruption
|
|
322
322
|
|
|
323
|
-
- Running
|
|
323
|
+
- Running Agent calls in the current wave keep going (Claude Code's Agent tool starts independent work)
|
|
324
324
|
- Next `/curdx-flow:start --resume` shows some tasks already committed
|
|
325
325
|
- Resume from the failing task
|
|
326
326
|
|
|
@@ -372,7 +372,7 @@ If the planner missed a dependency, `[P]` may be wrong. Solutions:
|
|
|
372
372
|
|
|
373
373
|
### 2. A wave too large
|
|
374
374
|
|
|
375
|
-
10+ parallel
|
|
375
|
+
10+ parallel Agent calls may trigger API rate limits or context pressure. Solutions:
|
|
376
376
|
- `max_parallel: 5` splits a big wave into several
|
|
377
377
|
- flow-planner avoids making waves too large when generating
|
|
378
378
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@curdx/flow",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "CLI installer for CurdX-Flow — AI engineering workflow meta-framework for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"curdx-flow": "bin/curdx-flow.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
+
"validate:contracts": "node scripts/validate-plugin-contracts.mjs",
|
|
10
11
|
"test": "node --test test/*.test.js",
|
|
11
|
-
"prepublishOnly": "node --test test/*.test.js && node bin/curdx-flow.js --version"
|
|
12
|
+
"prepublishOnly": "npm run validate:contracts && node --test test/*.test.js && node bin/curdx-flow.js --version"
|
|
12
13
|
},
|
|
13
14
|
"files": [
|
|
14
15
|
"bin/",
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://curdx-flow.dev/schemas/agent-frontmatter.schema.json",
|
|
4
|
+
"title": "CurdX-Flow Agent Frontmatter",
|
|
5
|
+
"description": "Supported YAML frontmatter fields for agents/*.md plugin subagent definitions.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["name", "description"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"name": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^[a-z0-9][a-z0-9-]{0,63}$"
|
|
13
|
+
},
|
|
14
|
+
"description": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"minLength": 1
|
|
17
|
+
},
|
|
18
|
+
"tools": {
|
|
19
|
+
"oneOf": [
|
|
20
|
+
{ "type": "string" },
|
|
21
|
+
{ "type": "array", "items": { "type": "string" } }
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"disallowedTools": {
|
|
25
|
+
"oneOf": [
|
|
26
|
+
{ "type": "string" },
|
|
27
|
+
{ "type": "array", "items": { "type": "string" } }
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"model": {
|
|
31
|
+
"type": "string"
|
|
32
|
+
},
|
|
33
|
+
"effort": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"enum": ["low", "medium", "high", "xhigh", "max"]
|
|
36
|
+
},
|
|
37
|
+
"maxTurns": {
|
|
38
|
+
"type": "integer",
|
|
39
|
+
"minimum": 1
|
|
40
|
+
},
|
|
41
|
+
"skills": {
|
|
42
|
+
"oneOf": [
|
|
43
|
+
{ "type": "string" },
|
|
44
|
+
{ "type": "array", "items": { "type": "string" } }
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"memory": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"enum": ["user", "project", "local"]
|
|
50
|
+
},
|
|
51
|
+
"background": {
|
|
52
|
+
"type": "boolean"
|
|
53
|
+
},
|
|
54
|
+
"isolation": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"enum": ["worktree"]
|
|
57
|
+
},
|
|
58
|
+
"color": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"enum": ["red", "blue", "green", "yellow", "purple", "orange", "pink", "cyan"]
|
|
61
|
+
},
|
|
62
|
+
"initialPrompt": {
|
|
63
|
+
"type": "string"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -35,6 +35,11 @@
|
|
|
35
35
|
"minimum": 1,
|
|
36
36
|
"default": 8,
|
|
37
37
|
"description": "Task count above which subagent strategy is preferred"
|
|
38
|
+
},
|
|
39
|
+
"wave_fail_policy": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"enum": ["continue-on-single", "stop-on-any"],
|
|
42
|
+
"default": "continue-on-single"
|
|
38
43
|
}
|
|
39
44
|
}
|
|
40
45
|
},
|
|
@@ -44,16 +49,16 @@
|
|
|
44
49
|
"properties": {
|
|
45
50
|
"always_on": {
|
|
46
51
|
"type": "array",
|
|
47
|
-
"items": { "
|
|
52
|
+
"items": { "$ref": "#/definitions/gateName" },
|
|
48
53
|
"default": ["karpathy-gate", "verification-gate"]
|
|
49
54
|
},
|
|
50
55
|
"standard_mode": {
|
|
51
56
|
"type": "array",
|
|
52
|
-
"items": { "
|
|
57
|
+
"items": { "$ref": "#/definitions/gateName" }
|
|
53
58
|
},
|
|
54
59
|
"enterprise_mode": {
|
|
55
60
|
"type": "array",
|
|
56
|
-
"items": { "
|
|
61
|
+
"items": { "$ref": "#/definitions/gateName" }
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
},
|
|
@@ -96,5 +101,20 @@
|
|
|
96
101
|
"type": "string",
|
|
97
102
|
"format": "date"
|
|
98
103
|
}
|
|
104
|
+
},
|
|
105
|
+
"definitions": {
|
|
106
|
+
"gateName": {
|
|
107
|
+
"type": "string",
|
|
108
|
+
"enum": [
|
|
109
|
+
"karpathy-gate",
|
|
110
|
+
"verification-gate",
|
|
111
|
+
"tdd-gate",
|
|
112
|
+
"coverage-audit-gate",
|
|
113
|
+
"adversarial-review-gate",
|
|
114
|
+
"edge-case-gate",
|
|
115
|
+
"security-gate",
|
|
116
|
+
"devex-gate"
|
|
117
|
+
]
|
|
118
|
+
}
|
|
99
119
|
}
|
|
100
120
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://curdx-flow.dev/schemas/gate-frontmatter.schema.json",
|
|
4
|
+
"title": "CurdX-Flow Gate Frontmatter",
|
|
5
|
+
"description": "Supported YAML frontmatter fields for gates/*.md quality gate definitions.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["gate", "category", "severity", "depends_on"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"gate": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^[a-z0-9][a-z0-9-]{0,63}$"
|
|
13
|
+
},
|
|
14
|
+
"category": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"enum": ["always-on", "standard-mode", "enterprise-mode"]
|
|
17
|
+
},
|
|
18
|
+
"severity": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"enum": ["blocking", "warning"]
|
|
21
|
+
},
|
|
22
|
+
"depends_on": {
|
|
23
|
+
"type": "array",
|
|
24
|
+
"items": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"pattern": "^[a-z0-9][a-z0-9-]{0,63}$"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://curdx-flow.dev/schemas/hooks.schema.json",
|
|
4
|
+
"title": "CurdX-Flow Hook Configuration",
|
|
5
|
+
"description": "Supported event names and handler shape for hooks/hooks.json.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["hooks"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"hooks": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"propertyNames": {
|
|
13
|
+
"enum": [
|
|
14
|
+
"SessionStart",
|
|
15
|
+
"InstructionsLoaded",
|
|
16
|
+
"UserPromptSubmit",
|
|
17
|
+
"UserPromptExpansion",
|
|
18
|
+
"PreToolUse",
|
|
19
|
+
"PermissionRequest",
|
|
20
|
+
"PostToolUse",
|
|
21
|
+
"PostToolUseFailure",
|
|
22
|
+
"PostToolBatch",
|
|
23
|
+
"PermissionDenied",
|
|
24
|
+
"Notification",
|
|
25
|
+
"SubagentStart",
|
|
26
|
+
"SubagentStop",
|
|
27
|
+
"TaskCreated",
|
|
28
|
+
"TaskCompleted",
|
|
29
|
+
"Stop",
|
|
30
|
+
"StopFailure",
|
|
31
|
+
"TeammateIdle",
|
|
32
|
+
"ConfigChange",
|
|
33
|
+
"CwdChanged",
|
|
34
|
+
"FileChanged",
|
|
35
|
+
"WorktreeCreate",
|
|
36
|
+
"WorktreeRemove",
|
|
37
|
+
"PreCompact",
|
|
38
|
+
"PostCompact",
|
|
39
|
+
"Elicitation",
|
|
40
|
+
"ElicitationResult",
|
|
41
|
+
"SessionEnd"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"additionalProperties": {
|
|
45
|
+
"type": "array",
|
|
46
|
+
"items": { "$ref": "#/definitions/matcherGroup" }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"definitions": {
|
|
51
|
+
"matcherGroup": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"required": ["hooks"],
|
|
54
|
+
"additionalProperties": false,
|
|
55
|
+
"properties": {
|
|
56
|
+
"matcher": { "type": "string" },
|
|
57
|
+
"hooks": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"items": { "$ref": "#/definitions/hookHandler" }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"hookHandler": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"required": ["type"],
|
|
66
|
+
"additionalProperties": true,
|
|
67
|
+
"properties": {
|
|
68
|
+
"type": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"enum": ["command", "http", "mcp_tool", "prompt", "agent"]
|
|
71
|
+
},
|
|
72
|
+
"if": { "type": "string" },
|
|
73
|
+
"timeout": { "type": "integer", "minimum": 1 },
|
|
74
|
+
"statusMessage": { "type": "string" },
|
|
75
|
+
"once": { "type": "boolean" },
|
|
76
|
+
"command": { "type": "string" },
|
|
77
|
+
"url": { "type": "string" },
|
|
78
|
+
"prompt": { "type": "string" },
|
|
79
|
+
"model": { "type": "string" }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://curdx-flow.dev/schemas/plugin-manifest.schema.json",
|
|
4
|
+
"title": "CurdX-Flow Claude Code Plugin Manifest",
|
|
5
|
+
"description": "Project-local schema for .claude-plugin/plugin.json.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["name", "version", "description"],
|
|
8
|
+
"additionalProperties": true,
|
|
9
|
+
"properties": {
|
|
10
|
+
"name": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
|
13
|
+
},
|
|
14
|
+
"version": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?$"
|
|
17
|
+
},
|
|
18
|
+
"description": { "type": "string" },
|
|
19
|
+
"author": { "type": "object" },
|
|
20
|
+
"homepage": { "type": "string" },
|
|
21
|
+
"repository": { "type": "string" },
|
|
22
|
+
"license": { "type": "string" },
|
|
23
|
+
"keywords": { "type": "array", "items": { "type": "string" } },
|
|
24
|
+
"skills": {
|
|
25
|
+
"oneOf": [
|
|
26
|
+
{ "type": "string" },
|
|
27
|
+
{ "type": "array", "items": { "type": "string" } }
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"agents": {
|
|
31
|
+
"oneOf": [
|
|
32
|
+
{ "type": "string" },
|
|
33
|
+
{ "type": "array", "items": { "type": "string" } }
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"hooks": {
|
|
37
|
+
"oneOf": [
|
|
38
|
+
{ "type": "string" },
|
|
39
|
+
{ "type": "object" }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"oneOf": [
|
|
44
|
+
{ "type": "string" },
|
|
45
|
+
{ "type": "object" }
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": {
|
|
51
|
+
"oneOf": [
|
|
52
|
+
{ "type": "string" },
|
|
53
|
+
{
|
|
54
|
+
"type": "object",
|
|
55
|
+
"required": ["name"],
|
|
56
|
+
"properties": {
|
|
57
|
+
"name": { "type": "string" },
|
|
58
|
+
"version": { "type": "string" },
|
|
59
|
+
"marketplace": { "type": "string" }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|