# prompt-journal 把你在 dsh 中发送的每一条需求,在**执行前**追加写入当前项目(会话 cwd)的 `prompts.md`,条目之间以 `\n\n---\n\n` 分隔。它是一个被动的观察者:只记录,不改变 任何消息、不拦截任何步骤;写入失败只打一条警告,绝不影响 agent 运行。 ## 记录什么 - 只记录**真实用户输入**(`source.kind === 'user'` 的消息):包括排队发送、 运行中途插入的 steering 输入,每条消息记为一个条目。 - 不记录:插件注入的上下文(如 agent-instructions、time-context)、工具结果、 子 agent(subagent)收到的委派任务。 ## 配置 | 字段 | 默认值 | 说明 | |---|---|---| | `file` | `prompts.md` | 日志文件,相对会话 cwd(也可用绝对路径) | | `separator` | `'\n\n---\n\n'` | 两个条目之间的分隔符 | 文件不存在时自动创建;已存在的文件只在末尾追加(去除尾部空白后拼接),已有内容 不会被改写。若部署没有注册 `fs` 提供方,插件以 no-op 挂载并警告一次。 ## 安装 ### 方式一:安装进 profile(推荐) ```sh dsh plugin --profile web add /d/projects/dsh-plugins/prompt-journal dsh --profile web --dump-config # 确认 prompt-journal 出现在配置树中 ``` ### 方式二:--patch overlay(本地开发,不安装) 写一个 `cordis.yml`: ```yaml - insert: - id: prompt-journal # Windows 下绝对路径必须写成 file:// URL name: 'file:///D:/projects/dsh-plugins/prompt-journal/index.ts' ``` 启动(需在仓库根目录先执行 `npm install`,插件的运行时依赖 `@deepseek-ai/schemastery` 从根 node_modules 解析): ```sh dsh web --patch ./cordis.yml ``` 随后在任意项目里发的需求都会写入该项目的 `prompts.md`。 ## 实现要点 - 监听 `agent/pre-step`(waterfall),以 `prepend` 占据链头:先落盘再调用 `next()`,保证"执行前写入",且不会被下游监听器短路掉。 - 会话项目目录取自 `agent.session.header.cwd`(durable header 中的已验证 绝对路径);写入走 `ctx.fs`(`resolve` → `stat`/`readText` → `writeText`), 因此沙箱/远程 fs 部署下日志会跟随该部署的文件系统视图。 - **沙箱策略**:写盘的 per-call policy 用 `ctx.sandboxPolicy.resolve({ session })` 从**会话**解析——会话 cwd 就是它的 workspace-write 边界(与工具层一致)。 若不传,沙箱 fs 会退回部署默认根(dsh 的启动目录),导致"从 A 目录启动、 项目在 B 目录"时写入被 `FS_SANDBOX_DENIED` 拒绝。会话被设为 read-only 时, 日志写入同样被拒(尊重会话策略),dsh 终端会有 warn 提示。 - **追加规则(分隔线永不重复)**:写入前检查日志文件结尾,分三种情况—— 空文件直接写条目;结尾已是分隔线(如手工补的 `---`,含旧式无空行写法) 则只补分隔线的"后半段"(空行)再接新条目;结尾是普通条目则插入完整 ` --- `。因此手工维护与插件写入可无缝混排。已知歧义:若上一条 需求自身恰以 `---` 行结尾,会被当作分隔线(极罕见,可手工修文件)。 - 同一目标的读-改-写按 `targetKey` 串行化(进程内 promise 链),多会话并发 追加同一文件不会交错覆盖。 - 所有 `ctx.on` / `ctx.effect` 注册随插件卸载自动撤销(可逆副作用)。 ## 调试 排查期插件会向 `~/.dsh/prompt-journal-debug.log` 追加每步痕迹(模块加载、 apply、pre-step、append ok/FAILED),每条需求仅数行,稳定后可移除。