@devflow-tools/cli 0.3.2 → 0.5.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/README.md +50 -0
- package/dist/commands/init.d.ts +6 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +53 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/plugin-setup.d.ts +23 -0
- package/dist/lib/plugin-setup.d.ts.map +1 -0
- package/dist/lib/plugin-setup.js +141 -0
- package/dist/lib/plugin-setup.js.map +1 -0
- package/dist/plugin-files/.claude-plugin/plugin.json +20 -0
- package/dist/plugin-files/CLAUDE.md +34 -0
- package/dist/plugin-files/dist/command-registry.json +235 -0
- package/dist/plugin-files/dist/skills/devflow:context/SKILL.md +29 -0
- package/dist/plugin-files/dist/skills/devflow:docker/SKILL.md +49 -0
- package/dist/plugin-files/dist/skills/devflow:electron/SKILL.md +49 -0
- package/dist/plugin-files/dist/skills/devflow:graph/SKILL.md +24 -0
- package/dist/plugin-files/dist/skills/devflow:graphql/SKILL.md +48 -0
- package/dist/plugin-files/dist/skills/devflow:knowledge/SKILL.md +24 -0
- package/dist/plugin-files/dist/skills/devflow:memory/SKILL.md +23 -0
- package/dist/plugin-files/dist/skills/devflow:nest/SKILL.md +48 -0
- package/dist/plugin-files/dist/skills/devflow:nextjs/SKILL.md +45 -0
- package/dist/plugin-files/dist/skills/devflow:performance/SKILL.md +49 -0
- package/dist/plugin-files/dist/skills/devflow:react/SKILL.md +57 -0
- package/dist/plugin-files/dist/skills/devflow:tailwind/SKILL.md +45 -0
- package/dist/plugin-files/dist/skills/devflow:taro/SKILL.md +48 -0
- package/dist/plugin-files/dist/skills/devflow:vue/SKILL.md +52 -0
- package/dist/plugin-files/dist/skills/devflow:workflow/SKILL.md +24 -0
- package/dist/plugin-files/hooks/constitution.md +20 -0
- package/dist/plugin-files/hooks/post-tool-use +106 -0
- package/dist/plugin-files/hooks/pre-tool-use +147 -0
- package/dist/plugin-files/hooks/session-start +96 -0
- package/dist/plugin-files/hooks/stop +31 -0
- package/dist/plugin-files/hooks.json +40 -0
- package/dist/plugin-files/package.json +29 -0
- package/package.json +25 -23
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devflow:tailwind
|
|
3
|
+
description: Tailwind 专家 — 响应式设计、主题定制、Purge 检查、Utility 审查
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<HARD-GATE>
|
|
7
|
+
收到 devflow:tailwind 时,必须先调用以下 MCP 工具之一:
|
|
8
|
+
tailwind_new_component
|
|
9
|
+
</HARD-GATE>
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 🌊 TAILWIND
|
|
14
|
+
|
|
15
|
+
你是一个 Tailwind CSS 专家。使用 utility-first 方法构建界面。遇到问题先查 Tailwind 官方文档。
|
|
16
|
+
|
|
17
|
+
## 适用场景
|
|
18
|
+
|
|
19
|
+
**新建 Tailwind 组件:语义结构 → 样式 → 响应式 → 暗色模式**
|
|
20
|
+
|
|
21
|
+
**Utility 审查:清除自定义 CSS → 统一为 Tailwind utility**
|
|
22
|
+
|
|
23
|
+
**主题迁移:分析现有设计 → 提取 Design Token → 生成配置**
|
|
24
|
+
|
|
25
|
+
## 核心规则
|
|
26
|
+
|
|
27
|
+
- 优先使用 Tailwind utility classes,避免自定义 CSS
|
|
28
|
+
- 使用 Tailwind 响应式断点(sm/md/lg/xl/2xl)
|
|
29
|
+
- 颜色和间距从 theme 配置中引用
|
|
30
|
+
- 复杂样式使用 @apply 抽取
|
|
31
|
+
- 使用 class 策略实现暗色模式
|
|
32
|
+
- 避免内联 style 和自定义 CSS
|
|
33
|
+
- 不确定 class 名称时先查 Tailwind 官方文档
|
|
34
|
+
|
|
35
|
+
## 当前版本
|
|
36
|
+
|
|
37
|
+
- tailwind-docs: 3.4.0 — https://tailwindcss.com/docs
|
|
38
|
+
|
|
39
|
+
## 工作流
|
|
40
|
+
|
|
41
|
+
| 场景 | 触发方式 |
|
|
42
|
+
|------|----------|
|
|
43
|
+
| 新建 Tailwind 组件:语义结构 → 样式 → 响应式 → 暗色模式 | 调用 MCP 工具 `tailwind_new_component` |
|
|
44
|
+
| Utility 审查:清除自定义 CSS → 统一为 Tailwind utility | 调用 MCP 工具 `tailwind_audit:utility` |
|
|
45
|
+
| 主题迁移:分析现有设计 → 提取 Design Token → 生成配置 | 调用 MCP 工具 `tailwind_migrate:theme` |
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devflow:taro
|
|
3
|
+
description: Taro 跨端专家 — 多端适配、小程序调试、性能优化、兼容方案
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<HARD-GATE>
|
|
7
|
+
收到 devflow:taro 时,必须先调用以下 MCP 工具之一:
|
|
8
|
+
taro_new_page
|
|
9
|
+
</HARD-GATE>
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 🛠️ TARO
|
|
14
|
+
|
|
15
|
+
你是一个 Taro 跨端开发专家。使用 Taro 内置组件和 API,确保多端兼容。遇到小程序问题先查文档,不凭经验猜测。
|
|
16
|
+
|
|
17
|
+
## 适用场景
|
|
18
|
+
|
|
19
|
+
**新建页面:** Taro 跨端页面 → 生成页面组件(兼容 H5/微信小程序),配置路由和导航栏。
|
|
20
|
+
|
|
21
|
+
**跨端兼容检查:扫描平台 API → 识别风险点 → 建议兼容方案**
|
|
22
|
+
|
|
23
|
+
**小程序调试:错误排查 → 包体积分析 → 性能检测**
|
|
24
|
+
|
|
25
|
+
**小程序性能优化:包体积 → 渲染优化 → 加载速度**
|
|
26
|
+
|
|
27
|
+
## 核心规则
|
|
28
|
+
|
|
29
|
+
- 使用 Taro 内置组件(View、Text、Image)替代 HTML 元素
|
|
30
|
+
- 使用 Taro API 替代浏览器 API
|
|
31
|
+
- 避免使用 window/document 等 DOM API
|
|
32
|
+
- 使用条件编译处理平台差异(process.env.TARO_ENV)
|
|
33
|
+
- 样式使用 px 单位(Taro 自动转换)
|
|
34
|
+
- 合理使用分包加载减少首屏体积
|
|
35
|
+
- 不确定跨端兼容性时先查 Taro 文档确认
|
|
36
|
+
|
|
37
|
+
## 当前版本
|
|
38
|
+
|
|
39
|
+
- taro-docs: 3.6.0 — https://docs.taro.zone/docs/
|
|
40
|
+
|
|
41
|
+
## 工作流
|
|
42
|
+
|
|
43
|
+
| 场景 | 触发方式 |
|
|
44
|
+
|------|----------|
|
|
45
|
+
| 新建页面: | 调用 MCP 工具 `taro_new_page` |
|
|
46
|
+
| 跨端兼容检查:扫描平台 API → 识别风险点 → 建议兼容方案 | 调用 MCP 工具 `taro_check:cross-platform` |
|
|
47
|
+
| 小程序调试:错误排查 → 包体积分析 → 性能检测 | 调用 MCP 工具 `taro_debug:miniapp` |
|
|
48
|
+
| 小程序性能优化:包体积 → 渲染优化 → 加载速度 | 调用 MCP 工具 `taro_optimize:performance` |
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devflow:vue
|
|
3
|
+
description: Vue 3 专家 — Bug 诊断、Composition API、响应式调试、组件重构
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<HARD-GATE>
|
|
7
|
+
收到 devflow:vue 时,必须先调用以下 MCP 工具之一:
|
|
8
|
+
vue_diagnose_bug / vue_new_component / vue_debug_reactivity
|
|
9
|
+
</HARD-GATE>
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 💚 VUE
|
|
14
|
+
|
|
15
|
+
你是一个 Vue 3 专家。使用 Composition API、<script setup> 和 TypeScript。遇到问题先查文档再给方案,不凭空猜测。
|
|
16
|
+
|
|
17
|
+
## 适用场景
|
|
18
|
+
|
|
19
|
+
**Bug 排查:** 组件渲染异常、响应式数据不同步 → 定位源码,检查 computed/watch 逻辑,搜索 Vue 官方文档验证 API 用法。
|
|
20
|
+
|
|
21
|
+
**新建组件:** 按照项目 SFC 规范生成组件(template + script setup + style scoped),附带 Props/Emits/Slots 类型定义。
|
|
22
|
+
|
|
23
|
+
**响应式调试:追踪数据流 → 定位问题 → 修复**
|
|
24
|
+
|
|
25
|
+
**提取 Composable:识别重复逻辑 → 抽取 → 替换引用**
|
|
26
|
+
|
|
27
|
+
**Pinia Store 重构:结构审查 → 拆分/合并 → 类型安全**
|
|
28
|
+
|
|
29
|
+
## 核心规则
|
|
30
|
+
|
|
31
|
+
- 使用 <script setup lang="ts"> 语法糖
|
|
32
|
+
- 使用 Composition API(ref、reactive、computed、watch)
|
|
33
|
+
- 状态管理使用 Pinia
|
|
34
|
+
- 可复用逻辑抽取为 composables
|
|
35
|
+
- 遇到不确定的 API 时先查 Vue 官方文档再回答
|
|
36
|
+
- 修复 Bug 前先排查响应式数据流
|
|
37
|
+
- 生成代码后必须通过 vue-tsc 类型检查
|
|
38
|
+
- 不能凭记忆猜测 API 行为,必须基于当前项目版本
|
|
39
|
+
|
|
40
|
+
## 当前版本
|
|
41
|
+
|
|
42
|
+
- vue-docs: 3.4.0 — https://vuejs.org/guide/introduction.html
|
|
43
|
+
|
|
44
|
+
## 工作流
|
|
45
|
+
|
|
46
|
+
| 场景 | 触发方式 |
|
|
47
|
+
|------|----------|
|
|
48
|
+
| Bug 排查: | 调用 MCP 工具 `vue_diagnose_bug` |
|
|
49
|
+
| 新建组件: | 调用 MCP 工具 `vue_new_component` |
|
|
50
|
+
| 响应式调试:追踪数据流 → 定位问题 → 修复 | 调用 MCP 工具 `vue_debug_reactivity` |
|
|
51
|
+
| 提取 Composable:识别重复逻辑 → 抽取 → 替换引用 | 调用 MCP 工具 `vue_extract:composable` |
|
|
52
|
+
| Pinia Store 重构:结构审查 → 拆分/合并 → 类型安全 | 调用 MCP 工具 `vue_refactor:pinia` |
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devflow:workflow
|
|
3
|
+
description: 执行标准化开发工作流 — Bug 修复、功能开发、代码审查
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<HARD-GATE>
|
|
7
|
+
收到 devflow:workflow 时,必须先调用以下 MCP 工具之一:
|
|
8
|
+
run_workflow
|
|
9
|
+
</HARD-GATE>
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# /devflow:workflow
|
|
14
|
+
|
|
15
|
+
## 执行
|
|
16
|
+
|
|
17
|
+
调用 `run_workflow` MCP 工具:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
type = "bugfix" | "feature" | "review"
|
|
21
|
+
input = 用户的查询内容
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
MCP 工具提供标准化的 DAG 步骤编排、人工确认节点、执行追踪和遥测记录。工作流执行过程中,MCP 工具会返回需要你介入的步骤,此时才根据步骤指引执行具体操作。
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<EXTREMELY_IMPORTANT>
|
|
2
|
+
你正在使用 DevFlow MCP Gateway。
|
|
3
|
+
|
|
4
|
+
## MCP 优先原则
|
|
5
|
+
|
|
6
|
+
收到 /devflow:* 命令时,必须先调用 SKILL.md 中列出的 MCP 工具。
|
|
7
|
+
Hook 会自动拦截不符合规范的原生工具调用,并按提示引导你使用正确的 MCP 工具。
|
|
8
|
+
|
|
9
|
+
正确流程:
|
|
10
|
+
1. 调用 Skill 加载命令
|
|
11
|
+
2. 立即调用 MCP 工具
|
|
12
|
+
3. 基于 MCP 结果回答
|
|
13
|
+
4. 如需补充信息,使用 Read 工具读取 MCP 未包含的关联文件
|
|
14
|
+
|
|
15
|
+
绝对不要:
|
|
16
|
+
- 在调用 MCP 之前用 grep/find/Glob/Explore 搜索代码
|
|
17
|
+
- 用 WebSearch/WebFetch 替代 get_knowledge
|
|
18
|
+
- 用 Agent(Explore) 替代 MCP 工具
|
|
19
|
+
- 尝试绕过 Hook 拦截
|
|
20
|
+
</EXTREMELY_IMPORTANT>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# PostToolUse Hook — DevFlow post-correction
|
|
5
|
+
# After AI uses a native tool that could have been an MCP tool, inject a corrective reminder.
|
|
6
|
+
|
|
7
|
+
INPUT_JSON=$(cat)
|
|
8
|
+
TOOL_NAME=$(python3 -c "
|
|
9
|
+
import sys, json
|
|
10
|
+
try:
|
|
11
|
+
data = json.load(sys.stdin)
|
|
12
|
+
print(data.get('tool_name', ''))
|
|
13
|
+
except:
|
|
14
|
+
print('')
|
|
15
|
+
" <<< "$INPUT_JSON" 2>/dev/null || echo "")
|
|
16
|
+
|
|
17
|
+
# Only interested in tools that could bypass MCP
|
|
18
|
+
case "$TOOL_NAME" in
|
|
19
|
+
Agent|Bash|Glob|Grep|WebSearch|WebFetch)
|
|
20
|
+
;;
|
|
21
|
+
*)
|
|
22
|
+
exit 0
|
|
23
|
+
;;
|
|
24
|
+
esac
|
|
25
|
+
|
|
26
|
+
# Determine project root and receipt file
|
|
27
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
28
|
+
RECEIPT_DIR="${TMPDIR:-/tmp}/.devflow-receipts"
|
|
29
|
+
mkdir -p "$RECEIPT_DIR"
|
|
30
|
+
SESSION_ID=$(echo -n "$PROJECT_ROOT" | shasum -a 256 2>/dev/null | cut -c1-16 || echo "default")
|
|
31
|
+
RECEIPT_FILE="$RECEIPT_DIR/${SESSION_ID}.json"
|
|
32
|
+
|
|
33
|
+
# Check if there was a recent MCP call (within 30s) — if yes, this native tool call is supplementary, not a bypass
|
|
34
|
+
HAS_RECENT_MCP=false
|
|
35
|
+
if [ -f "$RECEIPT_FILE" ]; then
|
|
36
|
+
last_call=$(python3 -c "
|
|
37
|
+
import json, sys, time
|
|
38
|
+
try:
|
|
39
|
+
with open('$RECEIPT_FILE') as f:
|
|
40
|
+
d = json.load(f)
|
|
41
|
+
ts = d.get('lastMcpCall', 0)
|
|
42
|
+
now = int(time.time())
|
|
43
|
+
if now - ts < 30:
|
|
44
|
+
print('valid')
|
|
45
|
+
except:
|
|
46
|
+
pass
|
|
47
|
+
" 2>/dev/null || echo "")
|
|
48
|
+
if [ "$last_call" = "valid" ]; then
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Read current bypass count
|
|
54
|
+
BYPASS_COUNT=0
|
|
55
|
+
if [ -f "$RECEIPT_FILE" ]; then
|
|
56
|
+
BYPASS_COUNT=$(python3 -c "
|
|
57
|
+
import json
|
|
58
|
+
with open('$RECEIPT_FILE') as f:
|
|
59
|
+
d = json.load(f)
|
|
60
|
+
print(d.get('bypassCount', 0))
|
|
61
|
+
" 2>/dev/null || echo "0")
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# If no bypasses yet, nothing to correct
|
|
65
|
+
if [ "$BYPASS_COUNT" -eq 0 ]; then
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Build corrective message based on tool type and count
|
|
70
|
+
case "$TOOL_NAME" in
|
|
71
|
+
Agent)
|
|
72
|
+
MESSAGE="第 ${BYPASS_COUNT} 次违规:你刚才使用了 Agent 子代理执行搜索。下次请用 get_project_context MCP 工具替代。"
|
|
73
|
+
;;
|
|
74
|
+
Bash)
|
|
75
|
+
MESSAGE="第 ${BYPASS_COUNT} 次违规:你刚才用了 Bash 命令搜索代码。下次请用 get_project_context MCP 工具替代。"
|
|
76
|
+
;;
|
|
77
|
+
Glob)
|
|
78
|
+
MESSAGE="第 ${BYPASS_COUNT} 次违规:你刚才用了 Glob 匹配文件。下次请用 get_project_context MCP 工具替代。"
|
|
79
|
+
;;
|
|
80
|
+
Grep)
|
|
81
|
+
MESSAGE="第 ${BYPASS_COUNT} 次违规:你刚才用了 Grep 搜索代码。下次请用 get_project_context MCP 工具替代。"
|
|
82
|
+
;;
|
|
83
|
+
WebSearch|WebFetch)
|
|
84
|
+
MESSAGE="第 ${BYPASS_COUNT} 次违规:你刚才用了网络搜索。下次请用 get_knowledge MCP 工具替代。"
|
|
85
|
+
;;
|
|
86
|
+
*)
|
|
87
|
+
exit 0
|
|
88
|
+
;;
|
|
89
|
+
esac
|
|
90
|
+
|
|
91
|
+
# Add enforcement status
|
|
92
|
+
if [ "$BYPASS_COUNT" -ge 3 ]; then
|
|
93
|
+
MESSAGE="${MESSAGE} 已达到最大违规次数,后续绕过将被硬阻止。"
|
|
94
|
+
elif [ "$BYPASS_COUNT" -ge 2 ]; then
|
|
95
|
+
MESSAGE="${MESSAGE} 下次违规将被阻止。"
|
|
96
|
+
else
|
|
97
|
+
MESSAGE="${MESSAGE} 请立即纠正。"
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# Output as additionalContext injection
|
|
101
|
+
if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
|
|
102
|
+
ESCAPED=$(printf '%s' "$MESSAGE" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
|
|
103
|
+
printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"%s"}}\n' "$ESCAPED"
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
exit 0
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# PreToolUse Hook — DevFlow unified command enforcement
|
|
5
|
+
# Reads command-registry.json and receipt to dynamically intercept
|
|
6
|
+
# native tool calls that bypass MCP Gateway.
|
|
7
|
+
|
|
8
|
+
# ---- Read stdin (Claude Code passes JSON via stdin) ----
|
|
9
|
+
INPUT_JSON=$(cat)
|
|
10
|
+
TOOL_NAME=$(python3 -c "
|
|
11
|
+
import sys, json
|
|
12
|
+
try:
|
|
13
|
+
data = json.load(sys.stdin)
|
|
14
|
+
print(data.get('tool_name', ''))
|
|
15
|
+
except:
|
|
16
|
+
print('')
|
|
17
|
+
" <<< "$INPUT_JSON" 2>/dev/null || echo "")
|
|
18
|
+
|
|
19
|
+
TOOL_INPUT=$(python3 -c "
|
|
20
|
+
import sys, json
|
|
21
|
+
try:
|
|
22
|
+
data = json.load(sys.stdin)
|
|
23
|
+
ti = data.get('tool_input', {})
|
|
24
|
+
if data.get('tool_name') == 'Agent':
|
|
25
|
+
out = {'subagent_type': ti.get('subagent_type',''),
|
|
26
|
+
'description': ti.get('description',''),
|
|
27
|
+
'prompt': ti.get('prompt','')}
|
|
28
|
+
print(json.dumps(out))
|
|
29
|
+
else:
|
|
30
|
+
print(ti.get('command') or ti.get('query') or ti.get('pattern') or json.dumps(ti))
|
|
31
|
+
except:
|
|
32
|
+
print('')
|
|
33
|
+
" <<< "$INPUT_JSON" 2>/dev/null || echo "")
|
|
34
|
+
|
|
35
|
+
# ---- Locate registry ----
|
|
36
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
37
|
+
RECEIPT_DIR="${TMPDIR:-/tmp}/.devflow-receipts"
|
|
38
|
+
SESSION_ID=$(echo -n "$PROJECT_ROOT" | shasum -a 256 2>/dev/null | cut -c1-16 || echo "default")
|
|
39
|
+
RECEIPT_FILE="$RECEIPT_DIR/${SESSION_ID}.json"
|
|
40
|
+
|
|
41
|
+
REGISTRY="${CLAUDE_PLUGIN_ROOT:-}/dist/command-registry.json"
|
|
42
|
+
if [ ! -f "$REGISTRY" ]; then
|
|
43
|
+
REGISTRY="$HOME/.devflow/command-registry.json"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Fallback: built-in default rules when registry is missing
|
|
47
|
+
if [ ! -f "$REGISTRY" ]; then
|
|
48
|
+
REGISTRY_CONTENT='{"commands":{"devflow:context":{"mcpTools":["get_project_context","search_symbol"],"blockedNative":["Grep","grep","find","rg","ag","Glob","Explore","WebSearch","WebFetch"]},"devflow:knowledge":{"mcpTools":["get_knowledge"],"blockedNative":["WebSearch","WebFetch"]},"devflow:graph":{"mcpTools":["get_dependency_graph"],"blockedNative":["Glob","Explore"]},"devflow:memory":{"mcpTools":["get_memory"],"blockedNative":["Grep","grep","find"]}}}'
|
|
49
|
+
else
|
|
50
|
+
REGISTRY_CONTENT=$(cat "$REGISTRY")
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# ---- Phase 1: Is this tool devflow-relevant? ----
|
|
54
|
+
is_devflow_relevant() {
|
|
55
|
+
local tool="$1" input="$2"
|
|
56
|
+
case "$tool" in
|
|
57
|
+
Bash)
|
|
58
|
+
echo "$input" | grep -qE "^(grep|find|rg|ag) " || return 1
|
|
59
|
+
# Exclude test/build/git commands
|
|
60
|
+
echo "$input" | grep -qE "(npm test|npm run|npx |git |python -m pytest|cargo test)" && return 1
|
|
61
|
+
return 0 ;;
|
|
62
|
+
Grep|Glob) return 0 ;;
|
|
63
|
+
Agent)
|
|
64
|
+
echo "$input" | grep -qiE "explore|search|find|code" && return 0
|
|
65
|
+
return 1 ;;
|
|
66
|
+
WebSearch|WebFetch) return 0 ;;
|
|
67
|
+
*) return 1 ;;
|
|
68
|
+
esac
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if ! is_devflow_relevant "$TOOL_NAME" "$TOOL_INPUT"; then
|
|
72
|
+
exit 0
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# ---- Phase 2: Read activeCommand from receipt ----
|
|
76
|
+
ACTIVE_CMD=$(RECEIPT_FILE="$RECEIPT_FILE" python3 -c "
|
|
77
|
+
import json, os
|
|
78
|
+
try:
|
|
79
|
+
with open(os.environ.get('RECEIPT_FILE','')) as f:
|
|
80
|
+
print(json.load(f).get('activeCommand',''))
|
|
81
|
+
except:
|
|
82
|
+
print('')
|
|
83
|
+
" 2>/dev/null || echo "")
|
|
84
|
+
|
|
85
|
+
# No activeCommand → no enforcement (user is not in a /devflow:* command)
|
|
86
|
+
[ -z "$ACTIVE_CMD" ] && exit 0
|
|
87
|
+
|
|
88
|
+
# ---- Phase 2.5: Dynamic interception from registry ----
|
|
89
|
+
MCP_INFO=$(ACTIVE_CMD="$ACTIVE_CMD" python3 -c "
|
|
90
|
+
import json, sys, os
|
|
91
|
+
try:
|
|
92
|
+
r = json.load(sys.stdin)
|
|
93
|
+
cmd = r['commands'].get(os.environ.get('ACTIVE_CMD',''), {})
|
|
94
|
+
tools = cmd.get('mcpTools', [])
|
|
95
|
+
blocked = cmd.get('blockedNative', [])
|
|
96
|
+
print(json.dumps({'tools': tools, 'blocked': blocked}))
|
|
97
|
+
except:
|
|
98
|
+
print('{}')
|
|
99
|
+
" <<< "$REGISTRY_CONTENT" 2>/dev/null || echo "{}")
|
|
100
|
+
|
|
101
|
+
MCP_TOOLS=$(echo "$MCP_INFO" | python3 -c "import sys,json; print(' / '.join(json.load(sys.stdin).get('tools',[])))" 2>/dev/null || echo "")
|
|
102
|
+
|
|
103
|
+
# Extract blocked list as newline-separated for membership check
|
|
104
|
+
BLOCKED=$(echo "$MCP_INFO" | python3 -c "
|
|
105
|
+
import sys, json
|
|
106
|
+
for item in json.load(sys.stdin).get('blocked', []):
|
|
107
|
+
print(item)
|
|
108
|
+
" 2>/dev/null || echo "")
|
|
109
|
+
|
|
110
|
+
# Check if current tool matches blockedNative from registry
|
|
111
|
+
IS_BLOCKED=false
|
|
112
|
+
case "$TOOL_NAME" in
|
|
113
|
+
Bash)
|
|
114
|
+
# Extract the base command (first word)
|
|
115
|
+
BASE_CMD=$(echo "$TOOL_INPUT" | awk '{print $1}')
|
|
116
|
+
case "$BASE_CMD" in
|
|
117
|
+
grep|find|rg|ag)
|
|
118
|
+
echo "$BLOCKED" | grep -qxF "$BASE_CMD" && IS_BLOCKED=true ;;
|
|
119
|
+
esac
|
|
120
|
+
;;
|
|
121
|
+
Grep)
|
|
122
|
+
echo "$BLOCKED" | grep -qxF "Grep" && IS_BLOCKED=true ;;
|
|
123
|
+
Glob)
|
|
124
|
+
echo "$BLOCKED" | grep -qxF "Glob" && IS_BLOCKED=true ;;
|
|
125
|
+
Agent)
|
|
126
|
+
# Agent with explore/search intent → check Explore in blocked
|
|
127
|
+
if echo "$TOOL_INPUT" | grep -qiE "explore|search|find|code"; then
|
|
128
|
+
echo "$BLOCKED" | grep -qxF "Explore" && IS_BLOCKED=true
|
|
129
|
+
fi
|
|
130
|
+
;;
|
|
131
|
+
WebSearch)
|
|
132
|
+
echo "$BLOCKED" | grep -qxF "WebSearch" && IS_BLOCKED=true ;;
|
|
133
|
+
WebFetch)
|
|
134
|
+
echo "$BLOCKED" | grep -qxF "WebFetch" && IS_BLOCKED=true ;;
|
|
135
|
+
esac
|
|
136
|
+
|
|
137
|
+
if [ "$IS_BLOCKED" = "true" ]; then
|
|
138
|
+
cat >&2 <<EOF
|
|
139
|
+
━━━━━━ DevFlow Gateway ━━━━━━
|
|
140
|
+
🛑 当前命令 ${ACTIVE_CMD} 禁止使用 ${TOOL_NAME}。
|
|
141
|
+
请使用 MCP 工具: ${MCP_TOOLS}
|
|
142
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
143
|
+
EOF
|
|
144
|
+
exit 2
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
exit 0
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Try to get project summary from devflow doctor
|
|
5
|
+
SUMMARY=""
|
|
6
|
+
DOCTOR_OUTPUT=""
|
|
7
|
+
|
|
8
|
+
# Try global devflow first, then npx fallback
|
|
9
|
+
if command -v devflow &>/dev/null; then
|
|
10
|
+
DOCTOR_OUTPUT=$(devflow doctor --json 2>/dev/null || echo "")
|
|
11
|
+
elif command -v npx &>/dev/null; then
|
|
12
|
+
DOCTOR_OUTPUT=$(npx --yes @devflow-tools/cli doctor --json 2>/dev/null || echo "")
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
if [ -n "$DOCTOR_OUTPUT" ] && echo "$DOCTOR_OUTPUT" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
|
|
16
|
+
PROJECT_NAME=$(echo "$DOCTOR_OUTPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('project',{}).get('name','') or d.get('projectName',''))" 2>/dev/null || echo "")
|
|
17
|
+
FILES=$(echo "$DOCTOR_OUTPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('project',{}).get('files', d.get('indexedFiles','?')))" 2>/dev/null || echo "?")
|
|
18
|
+
TECH=$(echo "$DOCTOR_OUTPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); s=d.get('project',{}).get('techStack',[]); print(', '.join(s) if s else 'unknown')" 2>/dev/null || echo "unknown")
|
|
19
|
+
ENGINES=$(echo "$DOCTOR_OUTPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); e=d.get('engines',{}); print(' '.join([f'{k} {\"✓\" if v else \"✗\"}' for k,v in e.items()]))" 2>/dev/null || echo "")
|
|
20
|
+
PLUGINS=$(echo "$DOCTOR_OUTPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); p=d.get('plugins',[]); print(', '.join(p))" 2>/dev/null || echo "")
|
|
21
|
+
|
|
22
|
+
SUMMARY="DevFlow 已就绪。项目 ${PROJECT_NAME:-unknown} (${TECH})。${FILES} 个文件已索引。引擎:${ENGINES}。已装插件:${PLUGINS}。使用 /devflow:context 查代码,/devflow:react 获取 React 专家能力。"
|
|
23
|
+
else
|
|
24
|
+
SUMMARY="DevFlow 插件已加载。如尚未初始化项目,请运行 devflow init。使用 /devflow 查看可用命令。"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Generate session token for MCP Gateway authentication
|
|
28
|
+
SESSION_TOKEN=""
|
|
29
|
+
if command -v devflow &>/dev/null; then
|
|
30
|
+
SESSION_TOKEN=$(devflow session token --expires 3600 2>/dev/null || echo "")
|
|
31
|
+
elif command -v npx &>/dev/null; then
|
|
32
|
+
SESSION_TOKEN=$(npx --yes @devflow-tools/cli session token --expires 3600 2>/dev/null || echo "")
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
TOKEN_VALUE=""
|
|
36
|
+
if [ -n "$SESSION_TOKEN" ] && echo "$SESSION_TOKEN" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
|
|
37
|
+
TOKEN_VALUE=$(echo "$SESSION_TOKEN" | python3 -c "import sys,json; print(json.load(sys.stdin).get('token',''))" 2>/dev/null || echo "")
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Clean up stale receipt from previous session
|
|
41
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
42
|
+
RECEIPT_DIR="${TMPDIR:-/tmp}/.devflow-receipts"
|
|
43
|
+
mkdir -p "$RECEIPT_DIR" 2>/dev/null || true
|
|
44
|
+
SESSION_ID=$(echo -n "$PROJECT_ROOT" | shasum -a 256 2>/dev/null | cut -c1-16 || echo "default")
|
|
45
|
+
RECEIPT_FILE="$RECEIPT_DIR/${SESSION_ID}.json"
|
|
46
|
+
OVERRIDE_FILE="$RECEIPT_DIR/${SESSION_ID}.override"
|
|
47
|
+
rm -f "$RECEIPT_FILE" "$OVERRIDE_FILE" 2>/dev/null || true
|
|
48
|
+
|
|
49
|
+
# Determine plugin root directory (same way as superpowers)
|
|
50
|
+
if [ -z "${CLAUDE_PLUGIN_ROOT:-}" ]; then
|
|
51
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
52
|
+
PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
53
|
+
else
|
|
54
|
+
PLUGIN_ROOT="$CLAUDE_PLUGIN_ROOT"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Read DevFlow Constitution from file (avoids shell escaping issues)
|
|
58
|
+
CONSTITUTION_FILE="${PLUGIN_ROOT}/skills/devflow/SKILL.md"
|
|
59
|
+
if [ -f "${PLUGIN_ROOT}/hooks/constitution.md" ]; then
|
|
60
|
+
CONSTITUTION_FILE="${PLUGIN_ROOT}/hooks/constitution.md"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if [ -f "$CONSTITUTION_FILE" ]; then
|
|
64
|
+
GLOBAL_CONSTRAINT=$(cat "$CONSTITUTION_FILE" 2>/dev/null || echo "DevFlow Constitution 加载失败")
|
|
65
|
+
else
|
|
66
|
+
GLOBAL_CONSTRAINT="DevFlow Constitution 文件未找到。使用 /devflow: 命令时请优先调用 MCP 工具。"
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Append Gateway status to summary
|
|
70
|
+
GATEWAY_STATUS="\n\n━━━━━━ DevFlow Gateway ━━━━━━\n"
|
|
71
|
+
# Check if devflow MCP server is configured (token will be auto-generated by ensureSessionToken)
|
|
72
|
+
if [ -f ~/.claude/.mcp.json ] && grep -q '"devflow"' ~/.claude/.mcp.json 2>/dev/null; then
|
|
73
|
+
GATEWAY_STATUS="${GATEWAY_STATUS}Status: 🟢 DevFlow MCP 已配置\nGate 层数: 4 (宪法 → 派发 → 拦截 → 纠正)\n"
|
|
74
|
+
elif [ -f .mcp.json ] && grep -q '"devflow"' .mcp.json 2>/dev/null; then
|
|
75
|
+
GATEWAY_STATUS="${GATEWAY_STATUS}Status: 🟢 DevFlow MCP 已配置(项目级)\nGate 层数: 4 (宪法 → 派发 → 拦截 → 纠正)\n"
|
|
76
|
+
else
|
|
77
|
+
GATEWAY_STATUS="${GATEWAY_STATUS}Status: 🔴 未配置 DevFlow MCP Server\n请将 devflow MCP 配置加入 .mcp.json\n"
|
|
78
|
+
fi
|
|
79
|
+
GATEWAY_STATUS="${GATEWAY_STATUS}━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
80
|
+
|
|
81
|
+
SUMMARY="${SUMMARY}${GLOBAL_CONSTRAINT}${GATEWAY_STATUS}"
|
|
82
|
+
|
|
83
|
+
# Escape for JSON
|
|
84
|
+
ESCAPED=$(echo "$SUMMARY" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
|
|
85
|
+
|
|
86
|
+
# Output context injection in Claude Code format
|
|
87
|
+
if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
|
|
88
|
+
if [ -n "$TOKEN_VALUE" ]; then
|
|
89
|
+
ESCAPED_TOKEN=$(echo "$TOKEN_VALUE" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
|
|
90
|
+
printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": %s\n },\n "env": {\n "DEVFLOW_SESSION_TOKEN": %s\n }\n}\n' "$ESCAPED" "$ESCAPED_TOKEN"
|
|
91
|
+
else
|
|
92
|
+
printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": %s\n }\n}\n' "$ESCAPED"
|
|
93
|
+
fi
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
exit 0
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Stop Hook — DevFlow session cleanup
|
|
5
|
+
# Cleans up receipt/override files and outputs violation summary.
|
|
6
|
+
|
|
7
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
8
|
+
RECEIPT_DIR="${TMPDIR:-/tmp}/.devflow-receipts"
|
|
9
|
+
SESSION_ID=$(echo -n "$PROJECT_ROOT" | shasum -a 256 2>/dev/null | cut -c1-16 || echo "default")
|
|
10
|
+
RECEIPT_FILE="$RECEIPT_DIR/${SESSION_ID}.json"
|
|
11
|
+
OVERRIDE_FILE="$RECEIPT_DIR/${SESSION_ID}.override"
|
|
12
|
+
|
|
13
|
+
if [ -f "$RECEIPT_FILE" ]; then
|
|
14
|
+
# Read and output summary
|
|
15
|
+
python3 -c "
|
|
16
|
+
import json
|
|
17
|
+
with open('$RECEIPT_FILE') as f:
|
|
18
|
+
d = json.load(f)
|
|
19
|
+
bypass = d.get('bypassCount', 0)
|
|
20
|
+
print(f'会话违规统计: {bypass}/3')
|
|
21
|
+
if bypass > 0:
|
|
22
|
+
print(f'最后一次拦截: {d.get(\"lastAttempt\", \"N/A\")}')
|
|
23
|
+
" 2>/dev/null || true
|
|
24
|
+
|
|
25
|
+
# Cleanup
|
|
26
|
+
rm -f "$RECEIPT_FILE"
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
rm -f "$OVERRIDE_FILE"
|
|
30
|
+
|
|
31
|
+
exit 0
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start\"",
|
|
10
|
+
"async": false
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"PreToolUse": [
|
|
16
|
+
{
|
|
17
|
+
"matcher": "Bash|Glob|Grep|WebSearch|WebFetch|Agent",
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/pre-tool-use\"",
|
|
22
|
+
"async": false
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"PostToolUse": [
|
|
28
|
+
{
|
|
29
|
+
"matcher": "Bash|Glob|Grep|WebSearch|WebFetch|Agent",
|
|
30
|
+
"hooks": [
|
|
31
|
+
{
|
|
32
|
+
"type": "command",
|
|
33
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/post-tool-use\"",
|
|
34
|
+
"async": false
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@devflow-tools/claude-code-plugin",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "DevFlow \u2014 Developer Intelligence Platform for Claude Code",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "npx tsx scripts/inject-gates.ts",
|
|
9
|
+
"deploy": "npx tsx scripts/inject-gates.ts --deploy"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"pi-package",
|
|
13
|
+
"skills",
|
|
14
|
+
"development",
|
|
15
|
+
"context",
|
|
16
|
+
"knowledge-base"
|
|
17
|
+
],
|
|
18
|
+
"pi": {
|
|
19
|
+
"skills": [
|
|
20
|
+
"./skills"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"hooks",
|
|
26
|
+
"hooks.json",
|
|
27
|
+
"skills"
|
|
28
|
+
]
|
|
29
|
+
}
|