dsh-plugin-prompt-tool 1.0.0 → 2.0.1
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 +38 -29
- package/engine/actions/append-context.mjs +49 -0
- package/engine/actions/assembly.mjs +150 -0
- package/engine/actions/catalog.mjs +92 -0
- package/engine/actions/content.mjs +205 -0
- package/engine/actions/decision.mjs +54 -0
- package/engine/actions/examples.mjs +11 -0
- package/engine/actions/guard.mjs +80 -0
- package/engine/actions/inbox-prepend.mjs +37 -0
- package/engine/actions/index.mjs +100 -0
- package/engine/actions/inject-text.mjs +22 -0
- package/engine/actions/pre-step-filter.mjs +55 -0
- package/engine/actions/registration.mjs +168 -0
- package/engine/actions/request-params.mjs +32 -0
- package/engine/actions/sdk-strip.mjs +30 -0
- package/engine/actions/shared.mjs +71 -0
- package/engine/actions.mjs +2 -931
- package/engine/branch.mjs +44 -0
- package/engine/compaction-epoch.mjs +31 -13
- package/engine/compositions/source/local/dev-tool-search.yml +11 -0
- package/engine/compositions/source/local/instruction-hint.yml +22 -1
- package/engine/compositions/source/local/rule-engine.yml +6 -0
- package/engine/condition.mjs +16 -3
- package/engine/conditions/anchor.mjs +32 -0
- package/engine/conditions/availability.mjs +47 -0
- package/engine/conditions/composite.mjs +83 -0
- package/engine/conditions/count.mjs +140 -0
- package/engine/conditions/evaluation.mjs +41 -0
- package/engine/conditions/examples.mjs +11 -0
- package/engine/conditions/index.mjs +85 -0
- package/engine/conditions/names.mjs +21 -0
- package/engine/conditions/phase.mjs +54 -0
- package/engine/conditions/preset.mjs +94 -0
- package/engine/conditions/scope.mjs +18 -0
- package/engine/conditions/session.mjs +22 -0
- package/engine/conditions/source.mjs +63 -0
- package/engine/conditions/subject.mjs +121 -0
- package/engine/conditions/text.mjs +44 -0
- package/engine/conditions/values.mjs +32 -0
- package/engine/dev-tool-search.mjs +213 -0
- package/engine/executor.mjs +55 -6
- package/engine/fillers.mjs +23 -16
- package/engine/instruction-hint.mjs +34 -22
- package/engine/layers.mjs +135 -49
- package/engine/order.mjs +4 -1
- package/engine/predicates.mjs +2 -753
- package/engine/rule-engine.mjs +40 -0
- package/engine/rule-runtime.mjs +110 -0
- package/engine/rule-spec.mjs +187 -0
- package/engine/schema.mjs +16 -5
- package/engine/shared.mjs +1 -1
- package/engine/strategies.mjs +2 -235
- package/engine/subagent-tool-policy-core.mjs +7 -7
- package/engine/subagent-tool-policy.mjs +20 -10
- package/engine/tool-config-engine.mjs +53 -32
- package/engine/trigger-spec.mjs +49 -133
- package/lib/client.js +11117 -10860
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +82 -139
- package/lib/index.mjs +826 -1087
- package/lib/migrate-rules.d.mts +1 -0
- package/lib/migrate-rules.mjs +39 -0
- package/lib/module-package-CHPFweqK.mjs +2 -0
- package/lib/module-package-Cnd1_mwx.mjs +2872 -0
- package/lib/{paths-BgL42sSM.d.mts → paths-CyP0IkaM.d.mts} +223 -108
- package/lib/preset-transfer.d.mts +2 -2
- package/lib/preset-transfer.mjs +3 -2
- package/lib/{module-package-Cm3fHHlp.mjs → rules-migration-DJ6vASB5.mjs} +13696 -15557
- package/modules/ponytail/module.yml +381 -147
- package/modules/skill-surface/module.yml +74 -0
- package/modules/tool-surface/module.yml +67 -0
- package/package.json +5 -1
- package/templates/10-pre-step.yml +26 -22
- package/templates/14-first-turn-anchor.yml +25 -21
- package/templates/15-guide-auto.yml +34 -19
- package/templates/16-custom-fallback.yml +27 -15
- package/templates/18-placeholder-env-facts.yml +23 -14
- package/templates/20-system-section.yml +16 -10
- package/templates/30-runtime-context.yml +13 -7
- package/templates/40-agent-request.yml +11 -8
- package/templates/50-llm-stream.yml +14 -6
- package/templates/60-tool-pipeline.yml +15 -9
- package/templates/65-turn-stop.yml +19 -12
- package/templates/66-subagent-start.yml +12 -7
- package/templates/70-subagent-maintenance.yml +35 -29
- package/templates/75-subagent-end.yml +10 -5
- package/templates/80-tool-surface.yml +66 -0
- package/templates/policies/legacy-defaults.yml +34 -0
- package/engine/compositions/source/local/prompt-config-engine.yml +0 -7
- package/engine/prompt-config-engine.mjs +0 -104
- package/engine/trigger-editor-meta.mjs +0 -48
- package/modules/ponytail/configs/0000-ponytail-rules.yml +0 -102
- package/modules/ponytail/configs/0010-ponytail-level-full.yml +0 -19
- package/modules/ponytail/configs/0020-ponytail-level-lite.yml +0 -19
- package/modules/ponytail/configs/0030-ponytail-level-ultra.yml +0 -19
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# 模板:工具面收窄(核心文件与任务集)+ 按需解锁
|
|
2
|
+
#
|
|
3
|
+
# 为什么:工具面实测 **156 个工具、描述合计 46894 字符(≈12K token)**,每个请求都付。
|
|
4
|
+
# 收窄到核心集后只剩约 10 个工具,其余按需解锁。
|
|
5
|
+
#
|
|
6
|
+
# 与 test/engine/declarations/tool-bootstrap.yml 的关键差别:那份原型**只在受控相位收窄**
|
|
7
|
+
# (`promoted: false`),晋升后原样放开;本模板**覆盖全部相位**——晋升后放开等于白省,
|
|
8
|
+
# 而工具描述是每请求的固定开销。若要旧语义,给 `phase` 加 `promoted: false` 并把
|
|
9
|
+
# `compacted` 拆成两条即可。
|
|
10
|
+
#
|
|
11
|
+
# 配套两件(缺一不可):
|
|
12
|
+
# 1. `dev_tool_search` 工具(组合源 engine/compositions/source/local/dev-tool-search.yml)
|
|
13
|
+
# ——唯一发现入口,模型靠它搜索并解锁。
|
|
14
|
+
# 2. `allowFrom`(engine/actions/assembly.mjs)——从历史 `tool/call` 参数回收解锁名单。
|
|
15
|
+
# 只挂插件不配 allowFrom 的话,解锁是**一次性的**(当次请求用完即被裁)。
|
|
16
|
+
#
|
|
17
|
+
# 工具名必须与**实际装配名**一致。本项目 harness 是 `pwsh`(不是 `bash`)、
|
|
18
|
+
# `edit` + `write`(不是 `str_replace_editor`)。写错名字有两个后果,都很隐蔽:
|
|
19
|
+
# - 写进 allow 的名字不存在 → `requireMatch` 触发 fail-open,**静默暴露完整目录**;
|
|
20
|
+
# - 该在的没写进去 → 该工具被裁掉,模型只能用别的方式硬凑。
|
|
21
|
+
#
|
|
22
|
+
# `requireMatch: true` 是刻意的:任一 allow 工具缺失(或模型解锁了一个不存在的名字)
|
|
23
|
+
# 就放弃裁剪、暴露完整目录。宁可多给上下文,也不静默裁成空目录——收窄错了会让当前
|
|
24
|
+
# 工作流调不到工具,那是比多花 token 严重得多的故障。
|
|
25
|
+
#
|
|
26
|
+
# **本模板走规则路径,不能声明 `waterfallPosition`**(那是声明 `triggers` 路径的字段,
|
|
27
|
+
# `compileRules` 会以 unknown fields 拒绝)。`assembly` 的改写发生在通道下游结算之后,
|
|
28
|
+
# 与相邻动作的先后由规则顺序与模块顺序决定。若确实需要 `outermost`(`prepend: true`,
|
|
29
|
+
# 保证本动作在普通注册之外先看到装配),改用声明路径(同 test/engine/declarations/
|
|
30
|
+
# tool-bootstrap.yml 的格式)而不是本模板。
|
|
31
|
+
id: tool-surface-resident
|
|
32
|
+
name: 工具面收窄(核心文件与任务集)
|
|
33
|
+
enabled: true
|
|
34
|
+
if:
|
|
35
|
+
# 覆盖全部相位。为什么不能只写 `phase: { promoteOn, includeSubagents }`:
|
|
36
|
+
# `promoted` 的缺省是 **true**(= 只匹配已晋升),不是「任意」;而 `compacted` 未声明才是
|
|
37
|
+
# 「不判该维度」(engine/conditions/phase.mjs:27-48)。更麻烦的是 `promoted: 'ignore'`
|
|
38
|
+
# 要求同时声明 `compacted`,而 `compacted` 只接受布尔值——**没有任何单节点组合能表达
|
|
39
|
+
# 「两个相位都命中」**,必须用 `any` 拼两支(phase.mjs:30-35 的注释正是在讲这个表达力边界)。
|
|
40
|
+
# 支 1 `promoted: true` → 已晋升(不论压缩与否)
|
|
41
|
+
# 支 2 `{compacted: true, promoted: false}` → 压缩后未晋升(受控相位的压缩回退)
|
|
42
|
+
# 未晋升且未压缩的**首轮不收窄**:与「首轮保全文」同一取舍——先让模型看到完整目录,
|
|
43
|
+
# 再随相位推进收窄。收窄的价值在于工具描述是**每请求固定开销**(实测 46894 字符),
|
|
44
|
+
# 而长会话必然进入上面两支,所以省下的正是大头。
|
|
45
|
+
any:
|
|
46
|
+
- phase: { promoteOn: either, includeSubagents: true, promoted: true }
|
|
47
|
+
- phase: { promoteOn: either, includeSubagents: true, compacted: true, promoted: false }
|
|
48
|
+
then:
|
|
49
|
+
- id: narrow-tools
|
|
50
|
+
kind: assembly
|
|
51
|
+
target:
|
|
52
|
+
tools:
|
|
53
|
+
allow:
|
|
54
|
+
- pwsh
|
|
55
|
+
- read
|
|
56
|
+
- write
|
|
57
|
+
- edit
|
|
58
|
+
- glob
|
|
59
|
+
- grep
|
|
60
|
+
- todo_write
|
|
61
|
+
- skill_search
|
|
62
|
+
- skill_load
|
|
63
|
+
- dev_tool_search
|
|
64
|
+
# 解锁项从 dev_tool_search 的持久调用参数回收({ tool, key } 精确匹配)。
|
|
65
|
+
allowFrom: { tool: dev_tool_search, key: toolNames }
|
|
66
|
+
requireMatch: true
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#旧业务默认的精确数据快照,仅供模板生成和显式离线迁移读取。引擎运行时不得读取此文件。
|
|
2
|
+
|
|
3
|
+
version: 1
|
|
4
|
+
fillers:
|
|
5
|
+
envFacts:
|
|
6
|
+
text: |-
|
|
7
|
+
Environment facts:
|
|
8
|
+
{{ENV_FACTS}}
|
|
9
|
+
skillCatalog:
|
|
10
|
+
text: |-
|
|
11
|
+
Available skills ({{SKILL_COUNT}}):
|
|
12
|
+
{{SKILLS_TEXT}}
|
|
13
|
+
params:
|
|
14
|
+
limit: 20
|
|
15
|
+
fields: name,description
|
|
16
|
+
whenToUseLabel: 适用:
|
|
17
|
+
unnamedLabel: (unnamed)
|
|
18
|
+
instructionHint:
|
|
19
|
+
projectTemplate: "Reference documents exist: {{FILES}} (project root: {{ROOT}})."
|
|
20
|
+
globalTemplate: "A user reference document exists: {{FILES}}."
|
|
21
|
+
suffixTemplate: They are reference documents about the user's environment and
|
|
22
|
+
workspace conventions, not task instructions. Reading the relevant file
|
|
23
|
+
before workspace tasks is recommended, but consult them only when you need
|
|
24
|
+
those details; the task itself never depends on them.
|
|
25
|
+
messageTemplate: |-
|
|
26
|
+
<system-reminder>
|
|
27
|
+
Reference documents exist: {{FILES}}. {{SUFFIX}}
|
|
28
|
+
</system-reminder>
|
|
29
|
+
promotionGate:
|
|
30
|
+
reasoningPattern: \bwe\b
|
|
31
|
+
reasoningNegativePattern: \blet me\b
|
|
32
|
+
reasoningFlags: gi
|
|
33
|
+
customFallback:
|
|
34
|
+
fallbackAfter: 1
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* prompt-config-engine — prompt-tool 的唯一提示词注入执行器(装配入口)。
|
|
3
|
-
*
|
|
4
|
-
* 本文件只做装配:
|
|
5
|
-
* - schema.mjs 提示词配置加载/归一化/权威校验
|
|
6
|
-
* - strategies.mjs 内置策略绑定 + 模板专属策略懒加载(strategyDir)
|
|
7
|
-
* - fillers.mjs placeholder 动态填充器(instruction-hint / env-facts / skill-catalog)
|
|
8
|
-
* - instruction-hint.mjs 通用指令文件提示实现(策略与 context-gate 共用)
|
|
9
|
-
* - layers.mjs 非 pre-step 五个官方层级接线
|
|
10
|
-
* - executor.mjs pre-step 消息批执行器(过滤/去重/合并/落位)
|
|
11
|
-
*
|
|
12
|
-
* agent.cordis.yml 引擎行:
|
|
13
|
-
* - id: prompt-config-engine
|
|
14
|
-
* name: ./engine/prompt-config-engine.mjs
|
|
15
|
-
* config:
|
|
16
|
-
* configsDir: ../prompt-configs # 提示词配置模块目录(相对本文件)
|
|
17
|
-
* strategyDir: ../strategies # 模板专属策略目录(可选,相对本文件)
|
|
18
|
-
*
|
|
19
|
-
* 铁律:任一提示词配置失败只跳过该提示词配置并 warnOnce;配置错误挂载时 fail loud。
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { readFileSync } from 'node:fs'
|
|
23
|
-
import { basename } from 'node:path'
|
|
24
|
-
import { fileURLToPath } from 'node:url'
|
|
25
|
-
import { createPromptConfigs, loadPromptConfigFiles, parsePromptConfigYaml } from './schema.mjs'
|
|
26
|
-
import { applyPromptConfigs } from './executor.mjs'
|
|
27
|
-
import { defineConfig, passthrough } from './fields.mjs'
|
|
28
|
-
|
|
29
|
-
/** Cordis plugin name used by loader diagnostics. */
|
|
30
|
-
export const name = 'prompt-config-engine'
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 等待全部可注入层级的宿主服务。preset 行在 agent 组合内解析这些服务
|
|
34
|
-
* (persona 依赖 systemPrompt,工具行依赖 tools,agent loop 依赖 llm)。
|
|
35
|
-
*/
|
|
36
|
-
export const inject = ['systemPrompt', 'tools', 'llm']
|
|
37
|
-
|
|
38
|
-
export { parsePromptConfigYaml, loadPromptConfigFiles, createPromptConfigs, applyPromptConfigs }
|
|
39
|
-
|
|
40
|
-
/** 物化清单里是否仍挂着官方指令加载行(负责人冲突事实)。 */
|
|
41
|
-
const OFFICIAL_INSTRUCTIONS_ROW = /dsh-agent-instructions|(^|\s)id:\s*agent-instructions\b/m
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 本 mount 的装配事实:来源 id(协调器注册键,取预设目录名)+ 是否仍挂着官方
|
|
45
|
-
* 指令行。协调器据此拒绝在官方负责人仍在时同时注入插件指令文件正文。
|
|
46
|
-
* 引擎直接以源码方式被引用(无物化组合文件)时按未知处理,不声明冲突。
|
|
47
|
-
*/
|
|
48
|
-
function compositionFacts(dirUrl) {
|
|
49
|
-
try {
|
|
50
|
-
const dir = new URL('..', dirUrl)
|
|
51
|
-
return {
|
|
52
|
-
sourceId: `preset:${basename(fileURLToPath(dir))}`,
|
|
53
|
-
officialInstructions: OFFICIAL_INSTRUCTIONS_ROW.test(readFileSync(new URL('../agent.cordis.yml', dirUrl), 'utf8')),
|
|
54
|
-
}
|
|
55
|
-
} catch {
|
|
56
|
-
return { sourceId: undefined, officialInstructions: false }
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* 引擎插件入口:config.configsDir 为提示词配置模块目录(相对本文件的 URL),
|
|
62
|
-
* config.strategyDir 为模板专属策略目录(可选)。引擎扫描目录内每个
|
|
63
|
-
* *.yml / *.yaml / *.json 并装配为提示词配置。
|
|
64
|
-
*/
|
|
65
|
-
/**
|
|
66
|
-
* 配置契约:白名单由字段声明派生(此前没有白名单,未知键被静默忽略)。
|
|
67
|
-
* 三个键都刻意用 passthrough 保住既有语义——迁移前它们对**非字符串或空串**是静默取默认值
|
|
68
|
-
* (`typeof x === 'string' && x.length > 0 ? x : 默认`),换成 text() 会把静默降级变成挂载期
|
|
69
|
-
* 报错,那是 B2 未授权的行为变更。归一化结果与迁移前逐字段一致,唯一新增的是「未知键报错」。
|
|
70
|
-
*
|
|
71
|
-
* `presetRoot` 是可选的预设根基准(绝对 file URL):引擎由插件包提供、不再位于
|
|
72
|
-
* `<预设根>/.engine/` 时,`templateFile` 的越界校验无法再从引擎位置推导,改由装配期注入。
|
|
73
|
-
*/
|
|
74
|
-
export const configContract = defineConfig({
|
|
75
|
-
configsDir: passthrough((value) => (typeof value === 'string' && value.length > 0 ? value : './prompt-configs')),
|
|
76
|
-
strategyDir: passthrough((value) => (typeof value === 'string' && value.length > 0 ? value : undefined)),
|
|
77
|
-
presetRoot: passthrough((value) => (typeof value === 'string' && value.length > 0 ? value : undefined)),
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
export function apply(ctx, config) {
|
|
81
|
-
const { configsDir: dirName, strategyDir: rawStrategyDir, presetRoot: rawPresetRoot } = configContract.parse(config, name)
|
|
82
|
-
const dirUrl = new URL(dirName.endsWith('/') ? dirName : `${dirName}/`, import.meta.url)
|
|
83
|
-
// strategyDir 在入口统一解析成绝对 URL:bindResolver 用 `new URL(x.mjs, strategyDir)`
|
|
84
|
-
// 懒加载,相对写法的 base 不是合法绝对 URL(ERR_INVALID_URL),会让整行挂载失败。
|
|
85
|
-
const strategyDir = rawStrategyDir === undefined
|
|
86
|
-
? undefined
|
|
87
|
-
: new URL(rawStrategyDir.endsWith('/') ? rawStrategyDir : `${rawStrategyDir}/`, import.meta.url).href
|
|
88
|
-
// 预设根基准(可选):装配期注入绝对 file URL;缺省时由 schema 按「引擎上一级目录」推导。
|
|
89
|
-
const templatePresetRoot = rawPresetRoot === undefined
|
|
90
|
-
? undefined
|
|
91
|
-
: new URL(rawPresetRoot.endsWith('/') ? rawPresetRoot : `${rawPresetRoot}/`)
|
|
92
|
-
const facts = compositionFacts(dirUrl)
|
|
93
|
-
applyPromptConfigs(ctx, createPromptConfigs(loadPromptConfigFiles(dirUrl), {
|
|
94
|
-
strategyDir,
|
|
95
|
-
// 注入预设根时(引擎由插件包提供):相对 templateFile 仍按**历史引擎位置**
|
|
96
|
-
// `<预设根>/.engine/` 解析,用户预设与提示词配置零改写;越界校验基准则是预设根本身。
|
|
97
|
-
...(templatePresetRoot === undefined
|
|
98
|
-
? {}
|
|
99
|
-
: { templateBaseUrl: new URL('.engine/prompt-config-engine.mjs', templatePresetRoot), templatePresetRoot }),
|
|
100
|
-
}), {
|
|
101
|
-
prepend: true,
|
|
102
|
-
...facts,
|
|
103
|
-
})
|
|
104
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/** 声明编辑目录:类别、支持面和执行点来自实际编译器,示例仅提供可编辑的起点。 */
|
|
2
|
-
import { ACTION_KINDS, actionExecutionPoint, actionSupportsWhen } from './actions.mjs'
|
|
3
|
-
import { COMPOSITE_OPERATORS, PREDICATE_FACTORIES, compileDeclaration, compileWhen } from './trigger-spec.mjs'
|
|
4
|
-
import { WATERFALL_POSITIONS } from './trigger.mjs'
|
|
5
|
-
|
|
6
|
-
const PREDICATE_EXAMPLES = {
|
|
7
|
-
text: { subject: 'userMessage', keys: ['keyword'] },
|
|
8
|
-
phase: { promoted: false },
|
|
9
|
-
source: { kind: 'user' },
|
|
10
|
-
count: { of: 'tool-call', per: 'turn', min: 1 },
|
|
11
|
-
names: { allow: ['bash'] },
|
|
12
|
-
session: { type: 'user/message', present: false },
|
|
13
|
-
preset: { presetId: 'preset-id' },
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const ACTION_EXAMPLES = {
|
|
17
|
-
'inject-text': { config: { id: 'notice', layer: 'pre-step', text: 'Context', modelScope: 'all' } },
|
|
18
|
-
assembly: { target: { sections: { add: [{ name: 'custom-context', text: 'Context' }] } } },
|
|
19
|
-
decision: { phase: 'pre', decision: 'ask' },
|
|
20
|
-
'append-context': { mode: 'context', text: 'Context' },
|
|
21
|
-
guard: { mask: { deny: ['bash'] }, reason: 'Tool disabled by this preset' },
|
|
22
|
-
'sdk-strip': { mask: { deny: ['bash'] } },
|
|
23
|
-
'request-params': { patch: { maxTokens: 4096 }, modelScope: 'all' },
|
|
24
|
-
'inbox-prepend': { target: 'next-turn', text: 'Context' },
|
|
25
|
-
'pre-step-filter': { blockPlugins: ['plugin-id'] },
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** 每次返回独立的纯数据;调用方编辑草稿不会污染后续请求或引擎目录。 */
|
|
29
|
-
export function getTriggerEditorMeta() {
|
|
30
|
-
const predicates = Object.keys(PREDICATE_FACTORIES).map((kind) => {
|
|
31
|
-
if (!Object.hasOwn(PREDICATE_EXAMPLES, kind)) throw new TypeError(`trigger-editor-meta: missing ${kind} predicate example`)
|
|
32
|
-
return { kind, example: { [kind]: structuredClone(PREDICATE_EXAMPLES[kind]) } }
|
|
33
|
-
})
|
|
34
|
-
for (const kind of COMPOSITE_OPERATORS) {
|
|
35
|
-
const child = { phase: { promoted: false } }
|
|
36
|
-
predicates.push({ kind, example: { [kind]: kind === 'not' ? child : [child] } })
|
|
37
|
-
}
|
|
38
|
-
for (const { example } of predicates) compileWhen(example)
|
|
39
|
-
|
|
40
|
-
const actions = Object.keys(ACTION_KINDS).map((kind) => {
|
|
41
|
-
if (!Object.hasOwn(ACTION_EXAMPLES, kind)) throw new TypeError(`trigger-editor-meta: missing ${kind} action example`)
|
|
42
|
-
const example = { kind, ...structuredClone(ACTION_EXAMPLES[kind]) }
|
|
43
|
-
const { channel, phase } = actionExecutionPoint(example)
|
|
44
|
-
compileDeclaration({ id: kind, channel, phase, do: example })
|
|
45
|
-
return { kind, example, channel, phase, supportsWhen: actionSupportsWhen(kind) }
|
|
46
|
-
})
|
|
47
|
-
return { predicates, actions, composites: [...COMPOSITE_OPERATORS], waterfallPositions: [...WATERFALL_POSITIONS] }
|
|
48
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
id: ponytail-rules
|
|
2
|
-
name: Ponytail 规则(公共部分,常驻)
|
|
3
|
-
layer: system-section
|
|
4
|
-
order: 190
|
|
5
|
-
enabled: true
|
|
6
|
-
strategy: static
|
|
7
|
-
text: |-
|
|
8
|
-
# Ponytail
|
|
9
|
-
|
|
10
|
-
You are a lazy senior developer. Lazy means efficient, not careless. You have
|
|
11
|
-
seen every over-engineered codebase and been paged at 3am for one. The best
|
|
12
|
-
code is the code never written.
|
|
13
|
-
|
|
14
|
-
## Persistence
|
|
15
|
-
|
|
16
|
-
ACTIVE EVERY RESPONSE. No drift back to over-building. Still active if
|
|
17
|
-
unsure. Default: **full**.
|
|
18
|
-
|
|
19
|
-
The active level is controlled by the preset's Ponytail configuration
|
|
20
|
-
and persists until that configuration is changed.
|
|
21
|
-
|
|
22
|
-
## The ladder
|
|
23
|
-
|
|
24
|
-
Stop at the first rung that holds:
|
|
25
|
-
|
|
26
|
-
1. **Does this need to exist at all?** Speculative need = skip it, say so in one line. (YAGNI)
|
|
27
|
-
2. **Already in this codebase?** A helper, util, type, or pattern that already lives here → reuse it. Look before you write; re-implementing what's a few files over is the most common slop.
|
|
28
|
-
3. **Stdlib does it?** Use it.
|
|
29
|
-
4. **Native platform feature covers it?** `<input type="date">` over a picker lib, CSS over JS, DB constraint over app code.
|
|
30
|
-
5. **Already-installed dependency solves it?** Use it. Never add a new one for what a few lines can do.
|
|
31
|
-
6. **Can it be one line?** One line.
|
|
32
|
-
7. **Only then:** the minimum code that works.
|
|
33
|
-
|
|
34
|
-
The ladder is a reflex, not a research project — but it runs *after* you
|
|
35
|
-
understand the problem, not instead of it. Read the task and the code it
|
|
36
|
-
touches first, trace the real flow end to end, then climb. Two rungs work →
|
|
37
|
-
take the higher one and move on. The first lazy solution that works is the
|
|
38
|
-
right one — once you actually know what the change has to touch.
|
|
39
|
-
|
|
40
|
-
**Bug fix = root cause, not symptom.** A report names a symptom. Before you
|
|
41
|
-
edit, grep every caller of the function you're about to touch. The lazy fix IS
|
|
42
|
-
the root-cause fix: one guard in the shared function is a smaller diff than a
|
|
43
|
-
guard in every caller — and patching only the path the ticket names leaves
|
|
44
|
-
every sibling caller still broken. Fix it once, where all callers route through.
|
|
45
|
-
|
|
46
|
-
## Rules
|
|
47
|
-
|
|
48
|
-
- No unrequested abstractions: no interface with one implementation, no factory for one product, no config for a value that never changes.
|
|
49
|
-
- No boilerplate, no scaffolding "for later", later can scaffold for itself.
|
|
50
|
-
- Deletion over addition. Boring over clever, clever is what someone decodes at 3am.
|
|
51
|
-
- Fewest files possible. Shortest working diff wins — but only once you understand the problem. The smallest change in the wrong place isn't lazy, it's a second bug.
|
|
52
|
-
- Complex request? Ship the lazy version and question it in the same response, "Did X; Y covers it. Need full X? Say so." Never stall on an answer you can default.
|
|
53
|
-
- Two stdlib options, same size? Take the one that's correct on edge cases. Lazy means writing less code, not picking the flimsier algorithm.
|
|
54
|
-
- Mark deliberate simplifications that cut a real corner with a known ceiling (global lock, O(n²) scan, naive heuristic) with a `ponytail:` comment naming the ceiling and upgrade path (`# ponytail: global lock, per-account locks if throughput matters`).
|
|
55
|
-
|
|
56
|
-
## Output
|
|
57
|
-
|
|
58
|
-
Code first. Then at most three short lines: what was skipped, when to add it.
|
|
59
|
-
No essays, no feature tours, no design notes. If the explanation is longer
|
|
60
|
-
than the code, delete the explanation, every paragraph defending a
|
|
61
|
-
simplification is complexity smuggled back in as prose. Explanation the user
|
|
62
|
-
explicitly asked for (a report, a walkthrough, per-phase notes) is not debt,
|
|
63
|
-
give it in full, the rule is only against unrequested prose.
|
|
64
|
-
|
|
65
|
-
Pattern: `[code] → skipped: [X], add when [Y].`
|
|
66
|
-
|
|
67
|
-
## When NOT to be lazy
|
|
68
|
-
|
|
69
|
-
Never simplify away: input validation at trust boundaries, error handling
|
|
70
|
-
that prevents data loss, security measures, accessibility basics, anything
|
|
71
|
-
explicitly requested. User insists on the full version → build it, no
|
|
72
|
-
re-arguing.
|
|
73
|
-
|
|
74
|
-
Never lazy about understanding the problem. The ladder shortens the
|
|
75
|
-
solution, never the reading. Trace the whole thing first — every file the
|
|
76
|
-
change touches, the actual flow — before picking a rung. Laziness that skips
|
|
77
|
-
comprehension to ship a small diff is the dangerous kind: it dresses up as
|
|
78
|
-
efficiency and ships a confident wrong fix. Read fully, then be lazy.
|
|
79
|
-
|
|
80
|
-
Hardware is never the ideal on paper: a real clock drifts, a real sensor
|
|
81
|
-
reads off, a PCA9685 runs a few percent fast. Leave the calibration knob, not
|
|
82
|
-
just less code, the physical world needs tuning a minimal model can't see.
|
|
83
|
-
|
|
84
|
-
Lazy code without its check is unfinished. Non-trivial logic (a branch, a
|
|
85
|
-
loop, a parser, a money/security path) leaves ONE runnable check behind, the
|
|
86
|
-
smallest thing that fails if the logic breaks: an `assert`-based
|
|
87
|
-
`demo()`/`__main__` self-check or one small `test_*.py`. No frameworks, no
|
|
88
|
-
fixtures, no per-function suites unless asked. Trivial one-liners need no
|
|
89
|
-
test, YAGNI applies to tests too.
|
|
90
|
-
|
|
91
|
-
## Boundaries
|
|
92
|
-
|
|
93
|
-
Ponytail governs what you build, not how you talk (pair with Caveman for
|
|
94
|
-
terse prose).
|
|
95
|
-
|
|
96
|
-
To deactivate, disable all Ponytail prompt configurations in the DSH
|
|
97
|
-
workbench or remove this card from the preset. This card does not
|
|
98
|
-
register /ponytail commands. Change levels through the preset configuration.
|
|
99
|
-
These DSH controls also apply when a Ponytail skill describes upstream
|
|
100
|
-
mode commands.
|
|
101
|
-
|
|
102
|
-
The shortest path to done is the right path.
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
id: ponytail-level-full
|
|
2
|
-
name: Ponytail 档位:full(标准档,默认)
|
|
3
|
-
layer: system-section
|
|
4
|
-
order: 200
|
|
5
|
-
enabled: true
|
|
6
|
-
strategy: static
|
|
7
|
-
group: ponytail-level
|
|
8
|
-
exclusive: true
|
|
9
|
-
text: |-
|
|
10
|
-
PONYTAIL MODE ACTIVE — level: full
|
|
11
|
-
|
|
12
|
-
## Intensity
|
|
13
|
-
|
|
14
|
-
| Level | What change |
|
|
15
|
-
|-------|------------|
|
|
16
|
-
| **full** | The ladder enforced. Stdlib and native first. Shortest diff, shortest explanation. Default. |
|
|
17
|
-
|
|
18
|
-
Example: "Add a cache for these API responses."
|
|
19
|
-
- full: "`@lru_cache(maxsize=1000)` on the fetch function. Skipped custom cache class, add when lru_cache measurably falls short."
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
id: ponytail-level-lite
|
|
2
|
-
name: Ponytail 档位:lite(轻量档)
|
|
3
|
-
layer: system-section
|
|
4
|
-
order: 201
|
|
5
|
-
enabled: false
|
|
6
|
-
strategy: static
|
|
7
|
-
group: ponytail-level
|
|
8
|
-
exclusive: true
|
|
9
|
-
text: |-
|
|
10
|
-
PONYTAIL MODE ACTIVE — level: lite
|
|
11
|
-
|
|
12
|
-
## Intensity
|
|
13
|
-
|
|
14
|
-
| Level | What change |
|
|
15
|
-
|-------|------------|
|
|
16
|
-
| **lite** | Build what's asked, but name the lazier alternative in one line. User picks. |
|
|
17
|
-
|
|
18
|
-
Example: "Add a cache for these API responses."
|
|
19
|
-
- lite: "Done, cache added. FYI: `functools.lru_cache` covers this in one line if you'd rather not own a cache class."
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
id: ponytail-level-ultra
|
|
2
|
-
name: Ponytail 档位:ultra(极端档)
|
|
3
|
-
layer: system-section
|
|
4
|
-
order: 202
|
|
5
|
-
enabled: false
|
|
6
|
-
strategy: static
|
|
7
|
-
group: ponytail-level
|
|
8
|
-
exclusive: true
|
|
9
|
-
text: |-
|
|
10
|
-
PONYTAIL MODE ACTIVE — level: ultra
|
|
11
|
-
|
|
12
|
-
## Intensity
|
|
13
|
-
|
|
14
|
-
| Level | What change |
|
|
15
|
-
|-------|------------|
|
|
16
|
-
| **ultra** | YAGNI extremist. Deletion before addition. Ship the one-liner and challenge the rest of the requirement in the same breath. |
|
|
17
|
-
|
|
18
|
-
Example: "Add a cache for these API responses."
|
|
19
|
-
- ultra: "No cache until a profiler says so. When it does: `@lru_cache`. A hand-rolled TTL cache class is a bug farm with a hit rate."
|