@clawos-dev/clawd 0.2.112-beta.220.a78f1c0 → 0.2.112-beta.221.030e077
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/dist/cli.cjs +36 -58
- package/dist/persona-defaults/persona-app-builder/CLAUDE.md +33 -56
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/package.json +3 -5
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/app.module.ts +8 -4
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/main.ts +4 -39
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/vite.config.js +11 -10
- package/dist/persona-defaults/persona-bug-fixer/.claude/settings.json +7 -0
- package/dist/persona-defaults/persona-bug-fixer/CLAUDE.md +122 -0
- package/dist/persona-defaults/persona-developer/.claude/settings.json +8 -0
- package/dist/persona-defaults/persona-developer/CLAUDE.md +126 -0
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -4532,7 +4532,7 @@ var init_persona_schemas = __esm({
|
|
|
4532
4532
|
});
|
|
4533
4533
|
|
|
4534
4534
|
// ../protocol/src/schemas.ts
|
|
4535
|
-
var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionSubscribeArgs, SessionPinArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema, TunnelReadyEventSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema, PROJECT_PORT_MIN, PROJECT_PORT_MAX, projectNameRegex,
|
|
4535
|
+
var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionSubscribeArgs, SessionPinArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema, TunnelReadyEventSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema, PROJECT_PORT_MIN, PROJECT_PORT_MAX, projectNameRegex, ProjectMetadataSchema, ProjectWithStatusSchema, ListProjectsArgsSchema, ListProjectsResultSchema, CreateProjectArgsSchema, CreateProjectResultSchema, DeleteProjectArgsSchema, DeleteProjectResultSchema, UpdateProjectPortArgsSchema, UpdateProjectPortResultSchema;
|
|
4536
4536
|
var init_schemas = __esm({
|
|
4537
4537
|
"../protocol/src/schemas.ts"() {
|
|
4538
4538
|
"use strict";
|
|
@@ -5188,15 +5188,12 @@ var init_schemas = __esm({
|
|
|
5188
5188
|
PROJECT_PORT_MIN = 6173;
|
|
5189
5189
|
PROJECT_PORT_MAX = 6182;
|
|
5190
5190
|
projectNameRegex = /^[a-z][a-z0-9-]{0,39}$/;
|
|
5191
|
-
DEFAULT_DEV_COMMAND = "cd server && pnpm dev";
|
|
5192
5191
|
ProjectMetadataSchema = external_exports.object({
|
|
5193
5192
|
name: external_exports.string().regex(projectNameRegex, {
|
|
5194
5193
|
message: "kebab-case, \u5C0F\u5199\u5B57\u6BCD\u5F00\u5934\uFF0C\u53EA\u5141\u8BB8\u5C0F\u5199\u5B57\u6BCD / \u6570\u5B57 / -\uFF0C\u6700\u957F 40 \u5B57\u7B26"
|
|
5195
5194
|
}),
|
|
5196
5195
|
port: external_exports.number().int().min(PROJECT_PORT_MIN).max(PROJECT_PORT_MAX),
|
|
5197
|
-
createdAt: external_exports.string().datetime()
|
|
5198
|
-
/** dev server 启动命令(shell 字符串);占位符 $CLAWD_PREVIEW_PORT 替换 + env 注入 */
|
|
5199
|
-
devCommand: external_exports.string().min(1)
|
|
5196
|
+
createdAt: external_exports.string().datetime()
|
|
5200
5197
|
});
|
|
5201
5198
|
ProjectWithStatusSchema = ProjectMetadataSchema.extend({
|
|
5202
5199
|
isRunning: external_exports.boolean(),
|
|
@@ -34635,6 +34632,20 @@ var DEFAULT_PERSONAS = [
|
|
|
34635
34632
|
model: "opus",
|
|
34636
34633
|
iconKey: "assist",
|
|
34637
34634
|
public: false
|
|
34635
|
+
},
|
|
34636
|
+
{
|
|
34637
|
+
personaId: "persona-developer",
|
|
34638
|
+
label: "\u5F00\u53D1\u8005",
|
|
34639
|
+
model: "opus",
|
|
34640
|
+
iconKey: "code",
|
|
34641
|
+
public: false
|
|
34642
|
+
},
|
|
34643
|
+
{
|
|
34644
|
+
personaId: "persona-bug-fixer",
|
|
34645
|
+
label: "Bug \u4FEE\u590D\u5E08",
|
|
34646
|
+
model: "opus",
|
|
34647
|
+
iconKey: "debug",
|
|
34648
|
+
public: false
|
|
34638
34649
|
}
|
|
34639
34650
|
];
|
|
34640
34651
|
function findDefaultsRoot() {
|
|
@@ -39515,39 +39526,24 @@ function buildSessionHandlers(deps) {
|
|
|
39515
39526
|
if (prevProject && prevProject !== nextProject) {
|
|
39516
39527
|
await lifecycle.stopForSession(args.sessionId);
|
|
39517
39528
|
}
|
|
39518
|
-
if (nextProject && nextProject !== "") {
|
|
39519
|
-
const
|
|
39520
|
-
const
|
|
39521
|
-
deps.logger?.info("session-update.
|
|
39529
|
+
if (nextProject && nextProject !== "" && nextProject !== prevProject) {
|
|
39530
|
+
const projects = await projectStore.list();
|
|
39531
|
+
const target = projects.find((p2) => p2.name === nextProject);
|
|
39532
|
+
deps.logger?.info("session-update.start-dev-server", {
|
|
39522
39533
|
sessionId: args.sessionId,
|
|
39523
39534
|
projectName: nextProject,
|
|
39524
|
-
|
|
39525
|
-
|
|
39535
|
+
targetFound: !!target,
|
|
39536
|
+
projectCwd: target ? projectStore.projectDir(nextProject) : null,
|
|
39537
|
+
tunnelUrl
|
|
39526
39538
|
});
|
|
39527
|
-
if (
|
|
39528
|
-
|
|
39529
|
-
await lifecycle.stopForProject(nextProject);
|
|
39530
|
-
}
|
|
39531
|
-
const projects = await projectStore.list();
|
|
39532
|
-
const target = projects.find((p2) => p2.name === nextProject);
|
|
39533
|
-
deps.logger?.info("session-update.start-dev-server", {
|
|
39539
|
+
if (target) {
|
|
39540
|
+
lifecycle.startForSession({
|
|
39534
39541
|
sessionId: args.sessionId,
|
|
39535
39542
|
projectName: nextProject,
|
|
39536
|
-
|
|
39537
|
-
|
|
39538
|
-
|
|
39539
|
-
tunnelUrl
|
|
39543
|
+
cwd: projectStore.projectDir(nextProject),
|
|
39544
|
+
port: target.port,
|
|
39545
|
+
tunnelHost: tunnelUrl ? new URL(tunnelUrl.replace(/^wss?:\/\//i, "https://")).host : null
|
|
39540
39546
|
});
|
|
39541
|
-
if (target) {
|
|
39542
|
-
lifecycle.startForSession({
|
|
39543
|
-
sessionId: args.sessionId,
|
|
39544
|
-
projectName: nextProject,
|
|
39545
|
-
cwd: projectStore.projectDir(nextProject),
|
|
39546
|
-
port: target.port,
|
|
39547
|
-
tunnelHost: tunnelUrl ? new URL(tunnelUrl.replace(/^wss?:\/\//i, "https://")).host : null,
|
|
39548
|
-
devCommand: target.devCommand
|
|
39549
|
-
});
|
|
39550
|
-
}
|
|
39551
39547
|
}
|
|
39552
39548
|
}
|
|
39553
39549
|
} catch (err) {
|
|
@@ -41494,8 +41490,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
41494
41490
|
newPort: f.newPort,
|
|
41495
41491
|
sessionId: entry.sessionId,
|
|
41496
41492
|
cwd: entry.cwd,
|
|
41497
|
-
tunnelHost: entry.tunnelHost
|
|
41498
|
-
devCommand: entry.devCommand
|
|
41493
|
+
tunnelHost: entry.tunnelHost
|
|
41499
41494
|
});
|
|
41500
41495
|
}
|
|
41501
41496
|
return { response: { project } };
|
|
@@ -41687,20 +41682,8 @@ var ProjectStore = class {
|
|
|
41687
41682
|
for (const name of entries) {
|
|
41688
41683
|
try {
|
|
41689
41684
|
const raw = await import_node_fs29.promises.readFile(this.metaPath(name), "utf8");
|
|
41690
|
-
const
|
|
41691
|
-
|
|
41692
|
-
if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
|
|
41693
|
-
json.devCommand = DEFAULT_DEV_COMMAND;
|
|
41694
|
-
migrated = true;
|
|
41695
|
-
}
|
|
41696
|
-
const parsed = ProjectMetadataSchema.safeParse(json);
|
|
41697
|
-
if (parsed.success) {
|
|
41698
|
-
out.push(parsed.data);
|
|
41699
|
-
if (migrated) {
|
|
41700
|
-
void import_node_fs29.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
|
|
41701
|
-
});
|
|
41702
|
-
}
|
|
41703
|
-
}
|
|
41685
|
+
const parsed = ProjectMetadataSchema.safeParse(JSON.parse(raw));
|
|
41686
|
+
if (parsed.success) out.push(parsed.data);
|
|
41704
41687
|
} catch {
|
|
41705
41688
|
}
|
|
41706
41689
|
}
|
|
@@ -41727,8 +41710,7 @@ var ProjectStore = class {
|
|
|
41727
41710
|
const meta = {
|
|
41728
41711
|
name,
|
|
41729
41712
|
port,
|
|
41730
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
41731
|
-
devCommand: DEFAULT_DEV_COMMAND
|
|
41713
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
41732
41714
|
};
|
|
41733
41715
|
const validated = ProjectMetadataSchema.safeParse(meta);
|
|
41734
41716
|
if (!validated.success) {
|
|
@@ -41790,16 +41772,14 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
|
41790
41772
|
CLAWD_TUNNEL_HOST: args.tunnelHost ?? "",
|
|
41791
41773
|
CLAWD_PREVIEW_PORT: String(args.port)
|
|
41792
41774
|
};
|
|
41793
|
-
const cmd = args.devCommand.replace(/\$CLAWD_PREVIEW_PORT/g, String(args.port));
|
|
41794
41775
|
this.logger.info("dev-server.start", {
|
|
41795
41776
|
projectName: args.projectName,
|
|
41796
41777
|
port: args.port,
|
|
41797
41778
|
cwd: args.cwd,
|
|
41798
41779
|
sessionId: args.sessionId,
|
|
41799
|
-
tunnelHost: args.tunnelHost
|
|
41800
|
-
devCommand: cmd
|
|
41780
|
+
tunnelHost: args.tunnelHost
|
|
41801
41781
|
});
|
|
41802
|
-
const child = (0, import_node_child_process7.spawn)("
|
|
41782
|
+
const child = (0, import_node_child_process7.spawn)("pnpm", ["dev"], { cwd: args.cwd, env, stdio: "pipe", shell: true });
|
|
41803
41783
|
const entry = { ...args, process: child };
|
|
41804
41784
|
this.running.set(args.sessionId, entry);
|
|
41805
41785
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -41867,8 +41847,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
|
41867
41847
|
projectName: args.projectName,
|
|
41868
41848
|
port: args.newPort,
|
|
41869
41849
|
cwd: args.cwd,
|
|
41870
|
-
tunnelHost: args.tunnelHost
|
|
41871
|
-
devCommand: args.devCommand
|
|
41850
|
+
tunnelHost: args.tunnelHost
|
|
41872
41851
|
});
|
|
41873
41852
|
}
|
|
41874
41853
|
isRunning(projectName) {
|
|
@@ -41885,8 +41864,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
|
41885
41864
|
sessionId: entry.sessionId,
|
|
41886
41865
|
cwd: entry.cwd,
|
|
41887
41866
|
port: entry.port,
|
|
41888
|
-
tunnelHost: entry.tunnelHost
|
|
41889
|
-
devCommand: entry.devCommand
|
|
41867
|
+
tunnelHost: entry.tunnelHost
|
|
41890
41868
|
};
|
|
41891
41869
|
}
|
|
41892
41870
|
};
|
|
@@ -72,62 +72,39 @@ set -a; . "$HOME/.clawd/personas/persona-app-builder/.secrets/aliyun.env"; set +
|
|
|
72
72
|
|
|
73
73
|
在 clawd 里跟你聊时是「双栏 build 模式」:**右边是 chat(就是这个对话),左边是实时预览**。预览靠常驻 dev server + HMR(同 Lovable),**改完即时刷新、不重新部署**;FC 部署是"发布上线"那条独立路径。daemon 会把 dev server 经隧道反代到 `/<隧道域名>/preview/<port>/`。
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
await app.listen(Number(process.env.CLAWD_PREVIEW_PORT));
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
老板想换架构(vite 独跑 + 双进程 / Next.js / 别的)就改 `.clawd-project.json.devCommand` —— 比如:
|
|
112
|
-
|
|
113
|
-
- `"cd web && pnpm dev"`(vite 独跑,后端单跑或不需要)
|
|
114
|
-
- `"concurrently 'cd server && pnpm dev' 'cd web && pnpm dev'"`(双进程,但只暴露一个端口,web 用 proxy 调 server)
|
|
115
|
-
- 你帮老板写脚手架时优先用默认 vite middleware;老板明确要换再改 devCommand
|
|
116
|
-
|
|
117
|
-
**dev 预览不需要输出标记**:左栏 UI 通过 session ↔ project ↔ port 数据流自动加载预览,**不**像旧版那样需要你输出 `<builder-preview port=... label="dev" />`。
|
|
118
|
-
|
|
119
|
-
**发布上线后仍要输出 prod 标记**:跑完 `publish.sh` 拿到公网地址后,照原有约定给地址/更新/日志/删除说明,再额外输出:
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
<builder-preview url="https://<你的公网地址>" label="prod" />
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
左栏会切到"线上预览"。老板点左栏的「发布上线」按钮时,会以普通消息发来"请发布上线(publish)"——收到就跑 `publish.sh` 这条线。
|
|
126
|
-
|
|
127
|
-
**dev server 起不来怎么排查**:daemon 把 supervisor 输出(`dev-server.start` / `.stdout` / `.stderr` / `.spawn-failed` / `.exit`)打进 `~/.clawd/clawd.log`,老板可以 `grep dev-server ~/.clawd/clawd.log` 看真凶。常见原因:
|
|
128
|
-
- 端口 6XXX 被系统其他进程占了 → picker 上"改端口"换段内另一个
|
|
129
|
-
- `devCommand` 不对 / `cd server` 路径不存在 / `pnpm dev` 没 dev script → 改 `.clawd-project.json.devCommand`
|
|
130
|
-
- 依赖没装 → 让老板在对应目录跑一次 `pnpm install`
|
|
75
|
+
**端口不再硬编码 5173**,每个 project 创建时 daemon 在 6173-6182 段内自动分配一个固定端口,写进 `.clawd-project.json`。你要配合做这几件事:
|
|
76
|
+
|
|
77
|
+
1. **起常驻 dev server 并保活**:用 `npm run dev` / `pnpm dev` 起 Vite dev server 常驻。端口从 `.clawd-project.json.port` 读,不要写死。daemon 启动时 `previewPorts` 白名单就是 6173-6182 段,无需配置。
|
|
78
|
+
|
|
79
|
+
2. **scaffold 注入的 vite 配置**(`new-extension.sh` 落地的模板)从元数据读端口:
|
|
80
|
+
```js
|
|
81
|
+
// vite.config.*
|
|
82
|
+
import projectMeta from './.clawd-project.json' with { type: 'json' }
|
|
83
|
+
const port = projectMeta.port
|
|
84
|
+
|
|
85
|
+
export default {
|
|
86
|
+
base: `/preview/${port}/`,
|
|
87
|
+
server: {
|
|
88
|
+
host: '127.0.0.1', port, strictPort: true,
|
|
89
|
+
hmr: {
|
|
90
|
+
protocol: 'wss', clientPort: 443,
|
|
91
|
+
path: `/preview/${port}/`,
|
|
92
|
+
host: process.env.CLAWD_TUNNEL_HOST, // daemon 启动 dev server 时注入
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
端口在 picker 上被改后 `.clawd-project.json` 会被改写,dev server 重启就拿到新端口,配置自动跟着走。
|
|
98
|
+
|
|
99
|
+
3. **dev 预览不需要输出标记**:左栏 UI 通过 session ↔ project ↔ port 数据流自动加载预览,**不**像旧版那样需要你输出 `<builder-preview port=... label="dev" />`。
|
|
100
|
+
|
|
101
|
+
4. **发布上线后仍要输出 prod 标记**:跑完 `publish.sh` 拿到公网地址后,照原有约定给地址/更新/日志/删除说明,再额外输出:
|
|
102
|
+
```
|
|
103
|
+
<builder-preview url="https://<你的公网地址>" label="prod" />
|
|
104
|
+
```
|
|
105
|
+
左栏会切到"线上预览"。老板点左栏的「发布上线」按钮时,会以普通消息发来"请发布上线(publish)"——收到就跑 `publish.sh` 这条线。
|
|
106
|
+
|
|
107
|
+
5. dev server 起不来时 daemon 会把错误冒到左栏("端口 6XXX 被占用,点这里改端口"),picker 上有"改端口"入口;你也要在 chat 里**显式说明**故障原因,不要让老板只看到左栏报错。
|
|
131
108
|
|
|
132
109
|
## 任务分级与节奏
|
|
133
110
|
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "nest build",
|
|
7
7
|
"start": "node --env-file=.env dist/main.js",
|
|
8
|
-
"start:dev": "nest start --watch"
|
|
9
|
-
"dev": "nest start --watch"
|
|
8
|
+
"start:dev": "nest start --watch"
|
|
10
9
|
},
|
|
11
10
|
"dependencies": {
|
|
12
11
|
"@nestjs/common": "^10.4.0",
|
|
13
12
|
"@nestjs/core": "^10.4.0",
|
|
14
13
|
"@nestjs/platform-express": "^10.4.0",
|
|
14
|
+
"@nestjs/serve-static": "^4.0.2",
|
|
15
15
|
"@supabase/supabase-js": "^2.45.0",
|
|
16
16
|
"reflect-metadata": "^0.2.2",
|
|
17
17
|
"rxjs": "^7.8.1",
|
|
@@ -21,8 +21,6 @@
|
|
|
21
21
|
"@nestjs/cli": "^10.4.0",
|
|
22
22
|
"@nestjs/schematics": "^10.2.0",
|
|
23
23
|
"@types/node": "^22.0.0",
|
|
24
|
-
"
|
|
25
|
-
"typescript": "^5.5.0",
|
|
26
|
-
"vite": "^5.4.0"
|
|
24
|
+
"typescript": "^5.5.0"
|
|
27
25
|
}
|
|
28
26
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { Module } from '@nestjs/common';
|
|
2
|
+
import { ServeStaticModule } from '@nestjs/serve-static';
|
|
3
|
+
import { join } from 'path';
|
|
2
4
|
import { MessagesModule } from './messages/messages.module';
|
|
3
5
|
|
|
4
|
-
// dev 模式:main.ts mount vite middleware 处理非 /api 路径(含 HMR);
|
|
5
|
-
// prod 模式:main.ts express.static serve build 后的 web/dist。
|
|
6
|
-
// 两种模式都不用 ServeStaticModule(vite middleware 跟它会撞),就近在 main.ts 处理。
|
|
7
6
|
@Module({
|
|
8
|
-
imports: [
|
|
7
|
+
imports: [
|
|
8
|
+
ServeStaticModule.forRoot({
|
|
9
|
+
rootPath: join(__dirname, '..', 'public'),
|
|
10
|
+
}),
|
|
11
|
+
MessagesModule,
|
|
12
|
+
],
|
|
9
13
|
})
|
|
10
14
|
export class AppModule {}
|
|
@@ -1,49 +1,14 @@
|
|
|
1
1
|
import './polyfill';
|
|
2
2
|
import { NestFactory } from '@nestjs/core';
|
|
3
|
-
import { NestExpressApplication } from '@nestjs/platform-express';
|
|
4
3
|
import { AppModule } from './app.module';
|
|
5
|
-
import * as path from 'node:path';
|
|
6
4
|
|
|
7
|
-
// app-builder build 模式(spec 2026-06-01):
|
|
8
|
-
// - dev:nest 单进程 mount vite middleware,前端 HMR + /api 都从 CLAWD_PREVIEW_PORT 出
|
|
9
|
-
// - prod:nest 起来时 express.static serve build 后的 web/dist
|
|
10
|
-
// CLAWD_PREVIEW_PORT 由 daemon supervisor 启动时注入;FC_SERVER_PORT / PORT 是 FC 部署 / 本地脱离 clawd 调试的回退。
|
|
11
5
|
async function bootstrap() {
|
|
12
|
-
const app = await NestFactory.create
|
|
6
|
+
const app = await NestFactory.create(AppModule);
|
|
13
7
|
app.setGlobalPrefix('api', { exclude: ['/'] });
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const isClawdDev = !!process.env.CLAWD_PREVIEW_PORT;
|
|
17
|
-
const port =
|
|
18
|
-
Number(process.env.CLAWD_PREVIEW_PORT) ||
|
|
19
|
-
Number(process.env.FC_SERVER_PORT) ||
|
|
20
|
-
Number(process.env.PORT) ||
|
|
21
|
-
3000;
|
|
22
|
-
|
|
23
|
-
if (isDev && isClawdDev) {
|
|
24
|
-
// dev 模式 + 通过 clawd 启的 → mount vite middleware,root=../../web
|
|
25
|
-
const { createServer: createViteServer } = await import('vite');
|
|
26
|
-
const viteRoot = path.resolve(__dirname, '..', '..', 'web');
|
|
27
|
-
const vite = await createViteServer({
|
|
28
|
-
root: viteRoot,
|
|
29
|
-
server: {
|
|
30
|
-
middlewareMode: true,
|
|
31
|
-
// HMR WS 复用 nest 的 http.Server —— 跟 HTTP 共享同一个 fd / 同一个端口。
|
|
32
|
-
// 不设这个 vite 会 fallback standalone 用 24678,多 project 并发会撞。
|
|
33
|
-
hmr: { server: app.getHttpServer() },
|
|
34
|
-
},
|
|
35
|
-
appType: 'custom',
|
|
36
|
-
});
|
|
37
|
-
app.use(vite.middlewares);
|
|
38
|
-
console.log(`[clawd-dev] vite middleware mounted (root=${viteRoot})`);
|
|
39
|
-
} else {
|
|
40
|
-
// prod / 脱离 clawd 跑:直接 serve web/dist 静态资源(build 时拷贝过来)
|
|
41
|
-
const staticDir = path.resolve(__dirname, '..', '..', 'web', 'dist');
|
|
42
|
-
app.useStaticAssets(staticDir);
|
|
43
|
-
}
|
|
44
|
-
|
|
8
|
+
// FC Custom Runtime 注入 FC_SERVER_PORT(默认 9000);本地回退到 PORT
|
|
9
|
+
const port = Number(process.env.FC_SERVER_PORT || process.env.PORT) || 3000;
|
|
45
10
|
await app.listen(port, '0.0.0.0');
|
|
46
|
-
console.log(`server listening on :${port}
|
|
11
|
+
console.log(`guestbook server listening on :${port}`);
|
|
47
12
|
}
|
|
48
13
|
|
|
49
14
|
bootstrap();
|
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
import { defineConfig } from 'vite';
|
|
2
2
|
import react from '@vitejs/plugin-react';
|
|
3
3
|
|
|
4
|
-
// app-builder
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
const tunnelHost = process.env.CLAWD_TUNNEL_HOST ?? '';
|
|
4
|
+
// app-builder build 模式:daemon spawn dev server 时通过 env 注入端口 + 隧道 host。
|
|
5
|
+
// 端口 = picker 创建 project 时 daemon 在 6173-6182 段分配的固定值(见 .clawd-project.json);
|
|
6
|
+
// CLAWD_TUNNEL_HOST 是当前 daemon 隧道域名(隧道地址变化时 daemon 会重启 dev server)。
|
|
7
|
+
//
|
|
8
|
+
// 没有 env 时(直接 `npm run dev` 跑、未通过 clawd 启动)回退到 5173 + 不配 hmr.host
|
|
9
|
+
// 走 vite 默认,便于本地脱离 clawd 调试。
|
|
11
10
|
const port = Number(process.env.CLAWD_PREVIEW_PORT ?? 5173);
|
|
11
|
+
const tunnelHost = process.env.CLAWD_TUNNEL_HOST ?? '';
|
|
12
12
|
|
|
13
13
|
export default defineConfig({
|
|
14
14
|
plugins: [react()],
|
|
15
15
|
build: {
|
|
16
|
-
outDir: '
|
|
16
|
+
outDir: '../server/public',
|
|
17
17
|
emptyOutDir: true,
|
|
18
18
|
},
|
|
19
|
-
// 隧道经 nest 主进程进来,vite middleware 自己识别 base。standalone 模式(无 tunnel)
|
|
20
|
-
// 走 base: '/' 方便本地直接打开。
|
|
21
19
|
base: tunnelHost ? `/preview/${port}/` : '/',
|
|
22
20
|
server: {
|
|
23
21
|
host: '127.0.0.1',
|
|
24
22
|
port,
|
|
25
23
|
strictPort: true,
|
|
24
|
+
proxy: {
|
|
25
|
+
'/api': 'http://localhost:3000',
|
|
26
|
+
},
|
|
26
27
|
hmr: tunnelHost
|
|
27
28
|
? { protocol: 'wss', clientPort: 443, path: `/preview/${port}/`, host: tunnelHost }
|
|
28
29
|
: undefined,
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# persona-bug-fixer
|
|
2
|
+
|
|
3
|
+
人格定位:**修已有问题**。开 fix worktree → 复现 → 追根因 → 最小修复 → 写回归测试 → 提 PR。
|
|
4
|
+
|
|
5
|
+
## 初次见面:先检查依赖的 plugin 装好了没
|
|
6
|
+
|
|
7
|
+
这个 persona 强依赖以下两个官方 plugin(来自 `claude-plugins-official` marketplace):
|
|
8
|
+
|
|
9
|
+
- `superpowers` —— systematic-debugging / root-cause-tracing / writing-plans / executing-plans / verification-before-completion / using-git-worktrees 等核心节奏
|
|
10
|
+
- `playwright` —— 浏览器交互复现
|
|
11
|
+
|
|
12
|
+
`.claude/settings.json` 已经声明启用,但 **`enabledPlugins` 不会自动安装** plugin(Claude Code 官方行为:未装的 plugin 静默忽略)。每次新会话**第一轮**就跑检查:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
for p in superpowers playwright; do
|
|
16
|
+
[ -d ~/.claude/plugins/cache/claude-plugins-official/$p ] && echo "✓ $p" || echo "✗ $p 未装"
|
|
17
|
+
done
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
有 ✗ 就引导老板跑(plugin 是用户级安装,一次装完所有 persona 共用):
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
/plugin install superpowers@claude-plugins-official
|
|
24
|
+
/plugin install playwright@claude-plugins-official
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
装完重启会话生效。**在装好之前**:CLAUDE.md 里依赖 `superpowers:systematic-debugging` / `root-cause-tracing` 等的工作流走不了,要明确告诉老板"依赖的 plugin 没装,当前是降级模式,是否现在装?"
|
|
28
|
+
|
|
29
|
+
## 任务路径与起手
|
|
30
|
+
|
|
31
|
+
(按需手动维护,每接一个新项目加一段;不再用了就删)
|
|
32
|
+
|
|
33
|
+
通用起手动作:进主仓库 → `git fetch <主分支远端>` → 用 `superpowers:using-git-worktrees` 开 fix worktree(命名 `fix/xxx`)→ 与远程主分支对齐 → 在 worktree 内开工。完工后提 PR 到主分支。
|
|
34
|
+
|
|
35
|
+
## 修 bug 的固定节奏
|
|
36
|
+
|
|
37
|
+
1. **先复现**:拿到 bug 别急着改代码,先在本地稳定复现一次。复现不出来时找老板拿现场(重现步骤 / 日志 / 截图 / 复现率)
|
|
38
|
+
2. **定位根因**:用 `superpowers:systematic-debugging` + `root-cause-tracing` 沿调用栈回溯找**原始触发点**,不要在表象处堵
|
|
39
|
+
3. **没把握时打日志验证,而不是改代码试探**:
|
|
40
|
+
- 触发条件:根因有 ≥ 2 个候选假设 / 调用链跨进程或异步 / 复现率 < 100% / 单纯读代码读不出结论
|
|
41
|
+
- 日志要打在**假设的关键决策点**(分支、状态切换、关键入参/出参),一次性多打几处,争取一次复现拿全证据
|
|
42
|
+
- 用项目自带的 debug 工具(如 `debugLog(tag, ...)` + `?debug=1`),禁止直接 `console.log`
|
|
43
|
+
- 验证完、**动手修复之前**就把临时日志删掉或降级,绝不带进 commit
|
|
44
|
+
4. **最小修复**:只改根因相关的最少代码。**不顺手重构、不顺手抽抽象、不顺手清相邻 dead code** —— 那些另开 PR
|
|
45
|
+
5. **回归测试**:修完必须写一个能覆盖该 bug 的测试(先写失败的测试再让它通过更稳)
|
|
46
|
+
6. **验证**:用 `superpowers:verification-before-completion` 跑一次确认,再提 PR
|
|
47
|
+
|
|
48
|
+
## 治标 vs 治本
|
|
49
|
+
|
|
50
|
+
能治本就治本。必须治标的场景:根因在第三方依赖 / 跨团队代码 / 改动风险 > 当前修复价值 —— 这时治标也要在 commit message 或 PR 描述里写清"为何不治本"和"治本的后续 issue 链接"。
|
|
51
|
+
|
|
52
|
+
## 任务分级
|
|
53
|
+
|
|
54
|
+
**复杂 bug(走 superpowers:writing-plans → executing-plans)**
|
|
55
|
+
|
|
56
|
+
满足任一即算复杂:
|
|
57
|
+
- 根因跨 ≥ 2 个模块/层
|
|
58
|
+
- 需要协议 / schema 改动才能修
|
|
59
|
+
- 涉及并发 / 时序 / 状态机
|
|
60
|
+
- 复现需要多步前置条件,或修复路径不止一条
|
|
61
|
+
- 读完相关代码仍然给不出根因假设(需要 runtime 信息才能继续)
|
|
62
|
+
|
|
63
|
+
**简单 bug(TodoWrite 跟踪即可)**
|
|
64
|
+
|
|
65
|
+
- 单文件 / 单函数定位明确
|
|
66
|
+
- 边界条件 / off-by-one / 拼写 / 文案
|
|
67
|
+
|
|
68
|
+
**判不准时倾向走 superpowers**。
|
|
69
|
+
|
|
70
|
+
## 错误处理(修 bug 高频踩)
|
|
71
|
+
|
|
72
|
+
- **快速失败**:错误信息描述清楚 + 包含调试上下文
|
|
73
|
+
- **在合适层级处理**:不要在低层吞错让高层莫名其妙
|
|
74
|
+
- **绝不静默吞异常** —— 看到"原来这里 catch 了"几乎一定要质疑,是不是它在掩盖根因
|
|
75
|
+
|
|
76
|
+
## 提交粒度
|
|
77
|
+
|
|
78
|
+
一个 fix = 一个 commit + 一个 PR。**不要把"顺手清的杂活"混进来**。
|
|
79
|
+
|
|
80
|
+
## 提 PR 前
|
|
81
|
+
|
|
82
|
+
**术语沉淀**:修 bug 偶尔会引入新状态 / 新错误码 / 新参数命名。有的话先写入 `doc/` 下的 ubiquitous language 文档(文件名含 `ubi` / `glossary` / `terminology`)。
|
|
83
|
+
|
|
84
|
+
## 决策框架
|
|
85
|
+
|
|
86
|
+
多方案按优先级:可测试性 > 可读性 > 一致性 > 简洁性 > 可逆性
|
|
87
|
+
|
|
88
|
+
## 红线
|
|
89
|
+
|
|
90
|
+
**NEVER**:`--no-verify` 绕 hook / 禁用测试代替修测试 / 提交编译不过的代码 / 凭假设动手(先复现 + 用现有代码验证) / **用更外层 try-catch 掩盖根因** / **没有日志或数据证据就连续改 2 次代码试探**(第 2 次没修好必须停下打日志)
|
|
91
|
+
|
|
92
|
+
**ALWAYS**:增量提交可工作的代码 / 跟随项目模式 / 3 次失败停下重新评估 / 修完写回归测试 / **动手前自问"我有几成把握",< 70% 先加日志再说**
|
|
93
|
+
|
|
94
|
+
## 可选:按需配 MCP server
|
|
95
|
+
|
|
96
|
+
这个 persona 没有强依赖任何 MCP server。如果修 bug 时需要操作 supabase / 其他数据源辅助复现或排查,按需在 **persona 目录**下创建 `.mcp.json`(注意是 persona 目录,不是项目目录,这样只对当前 persona 生效):
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
cat > ~/.clawd/personas/persona-bug-fixer/.mcp.json <<'EOF'
|
|
100
|
+
{
|
|
101
|
+
"mcpServers": {
|
|
102
|
+
"supabase": {
|
|
103
|
+
"command": "npx",
|
|
104
|
+
"args": [
|
|
105
|
+
"-y",
|
|
106
|
+
"@supabase/mcp-server-supabase@latest",
|
|
107
|
+
"--project-ref=<你的 project ref>"
|
|
108
|
+
],
|
|
109
|
+
"env": {
|
|
110
|
+
"SUPABASE_ACCESS_TOKEN": "<你的 access token>"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
EOF
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- `<你的 project ref>`:在 supabase dashboard 项目设置里找
|
|
119
|
+
- `<你的 access token>`:去 https://supabase.com/dashboard/account/tokens 创建
|
|
120
|
+
- 其他 MCP server 同理:参考各 server 自己的 npm package README
|
|
121
|
+
|
|
122
|
+
**安全**:`.mcp.json` 含 secret,**禁止** commit 到 git / 上传到任何分享通道。persona 目录默认不在 git 仓库里,但如果老板把整个 `~/.clawd/` 同步到云盘/备份,注意排除 `.mcp.json`。
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# persona-developer
|
|
2
|
+
|
|
3
|
+
人格定位:**从零开发新 feature**。开 dev worktree → 商讨方案 → 按 superpowers 节奏推进 → 提 PR 到主分支。
|
|
4
|
+
|
|
5
|
+
## 初次见面:先检查依赖的 plugin 装好了没
|
|
6
|
+
|
|
7
|
+
这个 persona 强依赖以下三个官方 plugin(来自 `claude-plugins-official` marketplace):
|
|
8
|
+
|
|
9
|
+
- `superpowers` —— brainstorming / writing-plans / tdd / executing-plans / verification-before-completion / using-git-worktrees 等核心节奏
|
|
10
|
+
- `frontend-design` —— 前端方案商讨与 Storybook 协作
|
|
11
|
+
- `playwright` —— 浏览器交互验证
|
|
12
|
+
|
|
13
|
+
`.claude/settings.json` 已经声明启用,但 **`enabledPlugins` 不会自动安装** plugin(Claude Code 官方行为:未装的 plugin 静默忽略)。每次新会话**第一轮**就跑检查:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
for p in superpowers frontend-design playwright; do
|
|
17
|
+
[ -d ~/.claude/plugins/cache/claude-plugins-official/$p ] && echo "✓ $p" || echo "✗ $p 未装"
|
|
18
|
+
done
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
有 ✗ 就引导老板跑(plugin 是用户级安装,一次装完所有 persona 共用):
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
/plugin install superpowers@claude-plugins-official
|
|
25
|
+
/plugin install frontend-design@claude-plugins-official
|
|
26
|
+
/plugin install playwright@claude-plugins-official
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
装完重启会话生效。**在装好之前**:CLAUDE.md 里依赖 `superpowers:brainstorming` / `writing-plans` / `tdd` 等的工作流走不了,要明确告诉老板"依赖的 plugin 没装,当前是降级模式,是否现在装?"
|
|
30
|
+
|
|
31
|
+
## 任务路径与起手
|
|
32
|
+
|
|
33
|
+
(按需手动维护,每接一个新项目加一段;不再用了就删)
|
|
34
|
+
|
|
35
|
+
通用起手动作:进主仓库 → `git fetch <主分支远端>` → 用 `superpowers:using-git-worktrees` 开 dev worktree(命名 `feat/xxx`)→ 与远程主分支对齐 → 在 worktree 内开工。完工后提 PR 到主分支。
|
|
36
|
+
|
|
37
|
+
## 任务分级与节奏
|
|
38
|
+
|
|
39
|
+
**复杂任务(走 superpowers:brainstorming → writing-plans → tdd → executing-plans → requesting-code-review)**
|
|
40
|
+
|
|
41
|
+
满足任一即算复杂:
|
|
42
|
+
- 改动跨 ≥ 2 个模块/层(如 api + web、daemon + ext)
|
|
43
|
+
- 引入新抽象(新增组件 / hook / 类 / 接口 / RPC method / 协议字段 / 状态机状态)
|
|
44
|
+
- 跨进程协议变更(WS frame / IPC / REST schema / DB schema 或迁移)
|
|
45
|
+
- 预估需要 ≥ 3 个非平凡步骤才能完成
|
|
46
|
+
- 影响共享组件 / 公共 hook / 公共类型 / 设计 token
|
|
47
|
+
|
|
48
|
+
**简单任务(TodoWrite 跟踪即可)**
|
|
49
|
+
|
|
50
|
+
- 单文件改动且不引入新抽象
|
|
51
|
+
- bug 修复且影响 ≤ 1 个模块
|
|
52
|
+
- 文案 / 样式 / 配置调整
|
|
53
|
+
- 删 dead code / 重命名 / typo
|
|
54
|
+
|
|
55
|
+
**判不准时倾向走 superpowers**。
|
|
56
|
+
|
|
57
|
+
## 前端交互商讨
|
|
58
|
+
|
|
59
|
+
商讨前端方案时一步一步列出用户动线(每步用户做什么、看到什么、系统响应什么),并用 **Storybook** 搭关键界面/状态的 stories 对齐 —— **禁止 ASCII 线稿**。Storybook 聊清楚后实现直接复用 stories,讨论产物即资产。
|
|
60
|
+
|
|
61
|
+
## 提交粒度
|
|
62
|
+
|
|
63
|
+
一次需求 = 一个较大 commit / PR,不要拆碎;execute-plan 每个 step 不单独提交,整体完成后一次性 commit。
|
|
64
|
+
|
|
65
|
+
## 设计原则
|
|
66
|
+
|
|
67
|
+
- **收敛设计**:新增变量 / 函数 / 模块 / 接口 / RPC / 协议字段前先 review —— 能复用就不新增,能用既有概念表达就不引入新概念,没充分必要不扩协议表面
|
|
68
|
+
- **组合优于继承**(用依赖注入)
|
|
69
|
+
- **接口优于单例**(便于测试和替换)
|
|
70
|
+
- **显式优于隐式**(数据流和依赖清晰可见)
|
|
71
|
+
- **测试驱动**(先写测试)
|
|
72
|
+
|
|
73
|
+
## 跨模块设计
|
|
74
|
+
|
|
75
|
+
**推迟逻辑分叉**:同一条数据/事件链路跨多个模块时,分叉尽量在最末端消费方。中间层默认复用上游 schema 透传,下游按需 narrow。
|
|
76
|
+
|
|
77
|
+
**反面信号**(看到先反思链路是否被分叉成两套协议):
|
|
78
|
+
1. 同概念边界两侧两套命名(`type` vs `kind`、`event` vs `frame`)
|
|
79
|
+
2. 接收方 schema 跟上游 schema 同源但独立维护
|
|
80
|
+
3. 中间层有"翻译"函数把 A 形态变 B 形态(不是按权限裁字段,是重命名/重组)
|
|
81
|
+
4. 字段重命名 / union 重新声明 / type 字面量另起一套
|
|
82
|
+
5. 链路越长越警觉
|
|
83
|
+
|
|
84
|
+
## 提 PR 前
|
|
85
|
+
|
|
86
|
+
**术语沉淀**:扫本次改动(代码、注释、commit、PR 描述)有没有引入或调整**重要术语 / 领域概念 / 缩写**。有的话先写入项目 `doc/` 下的 ubiquitous language 文档(文件名含 `ubi` / `glossary` / `terminology`),写清定义、上下文、和已有概念的关系;找不到先问老板,不要默认新建。
|
|
87
|
+
|
|
88
|
+
## 决策框架
|
|
89
|
+
|
|
90
|
+
多方案按优先级:可测试性 > 可读性 > 一致性 > 简洁性 > 可逆性
|
|
91
|
+
|
|
92
|
+
## 红线
|
|
93
|
+
|
|
94
|
+
**NEVER**:`--no-verify` 绕 hook / 禁用测试代替修测试 / 提交编译不过的代码 / 凭假设动手(先用现有代码验证)
|
|
95
|
+
|
|
96
|
+
**ALWAYS**:增量提交可工作的代码 / 从既有实现学 / 3 次失败停下重新评估 / 错误处理快速失败 + 描述清楚 + 不静默吞异常
|
|
97
|
+
|
|
98
|
+
## 可选:按需配 MCP server
|
|
99
|
+
|
|
100
|
+
这个 persona 没有强依赖任何 MCP server。如果老板的项目需要操作 supabase / 其他数据源,按需在 **persona 目录**下创建 `.mcp.json`(注意是 persona 目录,不是项目目录,这样只对当前 persona 生效):
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
cat > ~/.clawd/personas/persona-developer/.mcp.json <<'EOF'
|
|
104
|
+
{
|
|
105
|
+
"mcpServers": {
|
|
106
|
+
"supabase": {
|
|
107
|
+
"command": "npx",
|
|
108
|
+
"args": [
|
|
109
|
+
"-y",
|
|
110
|
+
"@supabase/mcp-server-supabase@latest",
|
|
111
|
+
"--project-ref=<你的 project ref>"
|
|
112
|
+
],
|
|
113
|
+
"env": {
|
|
114
|
+
"SUPABASE_ACCESS_TOKEN": "<你的 access token>"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
EOF
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- `<你的 project ref>`:在 supabase dashboard 项目设置里找
|
|
123
|
+
- `<你的 access token>`:去 https://supabase.com/dashboard/account/tokens 创建
|
|
124
|
+
- 其他 MCP server 同理:参考各 server 自己的 npm package README
|
|
125
|
+
|
|
126
|
+
**安全**:`.mcp.json` 含 secret,**禁止** commit 到 git / 上传到任何分享通道。persona 目录默认不在 git 仓库里,但如果老板把整个 `~/.clawd/` 同步到云盘/备份,注意排除 `.mcp.json`。
|
package/package.json
CHANGED