@godv61/dsh-task-engine 0.27.1 → 0.29.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/.acceptance.mjs +11 -11
- package/.adaptive-test.mjs +189 -0
- package/.assessment-batch1.mjs +14 -14
- package/.codex-project-test.mjs +80 -80
- package/.enforce-test.mjs +12 -12
- package/.evidence-test.mjs +8 -8
- package/.filter-test.mjs +6 -6
- package/.freeze-test.mjs +44 -44
- package/.hook-test.mjs +19 -2
- package/.p0-test.mjs +21 -21
- package/.preset-test.mjs +11 -1
- package/.revision-test.mjs +16 -16
- package/.roundtrip-test.mjs +247 -247
- package/.workflow-test.mjs +122 -120
- package/README.md +33 -27
- package/cordis.patch.yml +158 -9
- package/docs/CHANGELOG.md +44 -30
- package/docs/README.md +6 -5
- package/docs/adaptive-workflows.md +72 -0
- package/docs/configuration.md +25 -25
- package/docs/development.md +1 -1
- package/docs/faq.md +14 -6
- package/docs/getting-started.md +8 -5
- package/docs/manual-legacy.html +380 -0
- package/docs/manual.html +124 -378
- package/docs/resource-install.md +5 -5
- package/docs/roadmap.md +8 -9
- package/hooks/commit-msg +39 -22
- package/lib/adaptive.d.ts +15 -0
- package/lib/adaptive.js +54 -0
- package/lib/adaptive.js.map +1 -0
- package/lib/client.js +719 -433
- package/lib/client.js.map +3 -3
- package/lib/controller.d.ts +50 -0
- package/lib/controller.js +132 -0
- package/lib/controller.js.map +1 -1
- package/lib/dev-task.js +413 -18
- package/lib/dev-task.js.map +1 -1
- package/lib/engine.d.ts +20 -0
- package/lib/engine.js +4 -1
- package/lib/engine.js.map +1 -1
- package/lib/hook.js +50 -29
- package/lib/hook.js.map +1 -1
- package/lib/project-init.d.ts +23 -0
- package/lib/project-init.js +94 -0
- package/lib/project-init.js.map +1 -0
- package/lib/skill-audit.js +8 -7
- package/lib/skill-audit.js.map +1 -1
- package/lib/sonar.d.ts +33 -0
- package/lib/sonar.js +80 -0
- package/lib/sonar.js.map +1 -0
- package/package.json +15 -16
- package/preset/agent.cordis.yml +3 -3
- package/preset/enable.mjs +2 -2
- package/preset/persona.md +4 -2
- package/scripts/verify-package.mjs +9 -5
- package/skills/architecture-design/SKILL.md +11 -0
- package/skills/code-development/SKILL.md +11 -0
- package/skills/code-review/SKILL.md +11 -0
- package/skills/eng-delivery/SKILL.md +19 -15
- package/skills/requirements-analysis/SKILL.md +11 -0
- package/skills/task-orchestration/SKILL.md +11 -0
- package/skills/test-validation/SKILL.md +11 -0
package/.workflow-test.mjs
CHANGED
|
@@ -4,20 +4,20 @@ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'
|
|
|
4
4
|
import { tmpdir } from 'node:os'
|
|
5
5
|
import { join, resolve, sep } from 'node:path'
|
|
6
6
|
import { registerDevTask } from './lib/dev-task.js'
|
|
7
|
-
import { newTask, validateWorkflow } from './lib/engine.js'
|
|
8
|
-
import { adoptRecommendation, resolveFlow, retainStageBindings } from './lib/workflows.js'
|
|
9
|
-
import Controller from './lib/controller.js'
|
|
10
|
-
import { registerShippedSkills } from './lib/shipped-skills.js'
|
|
11
|
-
|
|
12
|
-
test('switching to minimal drops only stages outside its graph', () => {
|
|
13
|
-
const requirement = { skills: [{ skill: { source: 'project', name: 'requirements' }, rules: [] }] }
|
|
14
|
-
const development = { skills: [{ skill: { source: 'project', name: 'implement' }, rules: [] }] }
|
|
15
|
-
const result = retainStageBindings('minimal', { '需求': requirement, '开发': development })
|
|
16
|
-
assert.deepEqual(result.dropped, ['需求'])
|
|
17
|
-
assert.deepEqual(result.bindings, { '开发': development })
|
|
18
|
-
const resolved = resolveFlow('minimal', { flow: 'minimal', stage_bindings: result.bindings })
|
|
19
|
-
assert.deepEqual(validateWorkflow(resolved.config), [])
|
|
20
|
-
})
|
|
7
|
+
import { newTask, validateWorkflow } from './lib/engine.js'
|
|
8
|
+
import { adoptRecommendation, resolveFlow, retainStageBindings } from './lib/workflows.js'
|
|
9
|
+
import Controller from './lib/controller.js'
|
|
10
|
+
import { registerShippedSkills } from './lib/shipped-skills.js'
|
|
11
|
+
|
|
12
|
+
test('switching to minimal drops only stages outside its graph', () => {
|
|
13
|
+
const requirement = { skills: [{ skill: { source: 'project', name: 'requirements' }, rules: [] }] }
|
|
14
|
+
const development = { skills: [{ skill: { source: 'project', name: 'implement' }, rules: [] }] }
|
|
15
|
+
const result = retainStageBindings('minimal', { '需求': requirement, '开发': development })
|
|
16
|
+
assert.deepEqual(result.dropped, ['需求'])
|
|
17
|
+
assert.deepEqual(result.bindings, { '开发': development })
|
|
18
|
+
const resolved = resolveFlow('minimal', { flow: 'minimal', stage_bindings: result.bindings })
|
|
19
|
+
assert.deepEqual(validateWorkflow(resolved.config), [])
|
|
20
|
+
})
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* The preset as a project would actually run it: the skeleton plus the shipped
|
|
@@ -34,7 +34,7 @@ function adoptedFlow(id, extra) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
function fixture(stage = '开发', extra = {}, services = {}) {
|
|
37
|
+
function fixture(stage = '开发', extra = {}, services = {}) {
|
|
38
38
|
const cwd = resolve('test-project')
|
|
39
39
|
// A project running the standard flow: the skeleton plus the adopted
|
|
40
40
|
// recommendation, with one extra skill mounted on 完成 for the tests that need
|
|
@@ -46,15 +46,15 @@ function fixture(stage = '开发', extra = {}, services = {}) {
|
|
|
46
46
|
...adopted,
|
|
47
47
|
stage_bindings: {
|
|
48
48
|
...adopted.stage_bindings,
|
|
49
|
-
完成: { skills: [{ skill: { source: 'project', name: 'software-testing' }, rules: [] }] },
|
|
49
|
+
完成: { skills: [{ skill: { source: 'project', name: 'software-testing' }, rules: [] }] },
|
|
50
50
|
},
|
|
51
51
|
}).config
|
|
52
52
|
const state = newTask({ id: 'LIVE-1', title: 'EAM regression', branch: 'test', work_size: 'standard', risk_level: 'standard', flow: { flow: 'standard', version: 3, config }, root: cwd })
|
|
53
53
|
Object.assign(state, { stage, execution_version: 1, files: ['app.js'], requirement_confirmed: true, solution_confirmed: true, ...extra })
|
|
54
|
-
const records = new Map([[join(cwd, '.dsh/task-LIVE-1.json'), JSON.stringify(state)],
|
|
55
|
-
[join(cwd, 'app.js'), 'source'],
|
|
56
|
-
[join(cwd, '.dsh/skills/software-testing/SKILL.md'), 'test instructions'],
|
|
57
|
-
[join(cwd, '.dsh/skills/human-check/SKILL.md'), 'manual check instructions']])
|
|
54
|
+
const records = new Map([[join(cwd, '.dsh/task-LIVE-1.json'), JSON.stringify(state)],
|
|
55
|
+
[join(cwd, 'app.js'), 'source'],
|
|
56
|
+
[join(cwd, '.dsh/skills/software-testing/SKILL.md'), 'test instructions'],
|
|
57
|
+
[join(cwd, '.dsh/skills/human-check/SKILL.md'), 'manual check instructions']])
|
|
58
58
|
const events = [], runs = []
|
|
59
59
|
const session = { id: 'test-session', header: { cwd }, snapshotEvents: () => events }
|
|
60
60
|
const policy = { mode: 'workspace-write', workspaceRoot: cwd, sessionId: session.id }
|
|
@@ -86,99 +86,99 @@ function fixture(stage = '开发', extra = {}, services = {}) {
|
|
|
86
86
|
}
|
|
87
87
|
return { call: args => execute(JSON.parse(JSON.stringify({ task_id: 'LIVE-1', ...args })), exec), load, runs, policy, exec, records, cwd,
|
|
88
88
|
state: () => JSON.parse(records.get(join(cwd, '.dsh/task-LIVE-1.json'))) }
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
test('manual skill evidence is approved by the host without a shell command', async () => {
|
|
92
|
-
const config = resolveFlow('minimal', { flow: 'minimal', stage_bindings: {
|
|
93
|
-
'开发': { skills: [{ skill: { source: 'project', name: 'human-check' }, rules: [], evidence: 'manual' }] },
|
|
94
|
-
} }).config
|
|
95
|
-
const approvals = []
|
|
96
|
-
const f = fixture('开发', { flow: { flow: 'minimal', version: 3, config } }, {
|
|
97
|
-
approval: { request: async request => { approvals.push(request); return 'allowed-once' } },
|
|
98
|
-
})
|
|
99
|
-
f.load('human-check')
|
|
100
|
-
await f.call({ operation: 'skill_result', skill_name: 'human-check', evidence: ['checked by owner'] })
|
|
101
|
-
assert.equal(f.runs.length, 0)
|
|
102
|
-
assert.equal(approvals.length, 1)
|
|
103
|
-
assert.equal(f.state().skill_results['开发']['human-check'].approved, true)
|
|
104
|
-
assert.deepEqual(JSON.parse(await f.call({ operation: 'status' })).skill_blockers, [])
|
|
105
|
-
const denied = fixture('开发', { flow: { flow: 'minimal', version: 3, config } }, {
|
|
106
|
-
approval: { request: async () => 'rejected' },
|
|
107
|
-
})
|
|
108
|
-
denied.load('human-check')
|
|
109
|
-
await assert.rejects(denied.call({ operation: 'skill_result', skill_name: 'human-check', evidence: ['claimed approval'] }), /not approved/)
|
|
110
|
-
assert.equal(denied.state().skill_results?.['开发']?.['human-check'], undefined)
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
test('completion rejects unresolved terminal skills and rules', async () => {
|
|
114
|
-
const config = resolveFlow('minimal', { flow: 'minimal', stage_bindings: {
|
|
115
|
-
'交付': { skills: [{ skill: { source: 'project', name: 'must-run' }, rules: [{ source: 'project', name: 'missing' }], evidence: 'command' }] },
|
|
116
|
-
} }).config
|
|
117
|
-
const f = fixture('交付', {
|
|
118
|
-
flow: { flow: 'minimal', version: 3, config },
|
|
119
|
-
items: [{ id: 'A', title: 'small', status: 'done', review: { spec: { outcome: 'pass' }, quality: { outcome: 'pass' } } }],
|
|
120
|
-
commits: [{ label: 'TASK', hash: 'abcdef1' }],
|
|
121
|
-
})
|
|
122
|
-
const status = JSON.parse(await f.call({ operation: 'status' }))
|
|
123
|
-
assert.ok(status.skill_blockers.length > 0)
|
|
124
|
-
assert.ok(status.missing_rules.length > 0)
|
|
125
|
-
await assert.rejects(f.call({ operation: 'complete' }), /cannot complete.*must-run|cannot complete.*missing/)
|
|
126
|
-
assert.equal(f.state().completed, undefined)
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
test('same-named skill and rule freeze separately and disclose the rule body', async () => {
|
|
130
|
-
const f = fixture()
|
|
131
|
-
f.records.delete(join(f.cwd, '.dsh/task-LIVE-1.json'))
|
|
132
|
-
f.records.set(join(f.cwd, '.dsh/eng.json'), JSON.stringify({ flow: 'minimal', stage_bindings: {
|
|
133
|
-
'开发': { skills: [{ skill: { source: 'project', name: 'custom' }, rules: [{ source: 'project', name: 'custom' }], evidence: 'none' }] },
|
|
134
|
-
} }))
|
|
135
|
-
f.records.set(join(f.cwd, '.dsh/skills/custom/SKILL.md'), 'SKILL BODY')
|
|
136
|
-
f.records.set(join(f.cwd, '.dsh/rules/custom.md'), 'RULE BODY')
|
|
137
|
-
await f.call({ operation: 'create', title: 'collision', branch: 'main', files: ['app.js'] })
|
|
138
|
-
const resources = f.state().flow.resources.filter(resource => resource.ref.name === 'custom')
|
|
139
|
-
assert.deepEqual(resources.map(resource => resource.kind).sort(), ['rule', 'skill'])
|
|
140
|
-
assert.equal(JSON.parse(await f.call({ operation: 'status' })).bindings.rules[0].content, 'RULE BODY')
|
|
141
|
-
f.records.set(join(f.cwd, '.dsh/skills/custom/SKILL.md'), 'CHANGED SKILL')
|
|
142
|
-
const status = JSON.parse(await f.call({ operation: 'status' }))
|
|
143
|
-
assert.equal(status.bindings.skill_contents[0].content, 'CHANGED SKILL')
|
|
144
|
-
assert.ok(status.changed_source_skills.some(ref => ref.includes('project:custom')))
|
|
145
|
-
f.records.delete(join(f.cwd, '.dsh/skills/custom/SKILL.md'))
|
|
146
|
-
const missing = JSON.parse(await f.call({ operation: 'status' }))
|
|
147
|
-
assert.ok(missing.missing_skills.includes('project:custom'))
|
|
148
|
-
assert.equal(missing.bindings.skill_contents.length, 0)
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
test('task creation applies skill_profiles rules even when legacy inline bindings are empty', async () => {
|
|
152
|
-
const f = fixture()
|
|
153
|
-
const skill = { source: 'project', name: 'custom' }
|
|
154
|
-
const rule = { source: 'project', name: 'my-rule' }
|
|
155
|
-
f.records.delete(join(f.cwd, '.dsh/task-LIVE-1.json'))
|
|
156
|
-
f.records.set(join(f.cwd, '.dsh/eng.json'), JSON.stringify({ flow: 'minimal',
|
|
157
|
-
stage_bindings: {
|
|
158
|
-
'开发': { skills: [{ skill, rules: [], evidence: 'none' }] },
|
|
159
|
-
'交付': { skills: [{ skill, rules: [], evidence: 'none' }] },
|
|
160
|
-
},
|
|
161
|
-
skill_profiles: { 'project:custom': { rules: [rule], evidence: 'none' } },
|
|
162
|
-
}))
|
|
163
|
-
f.records.set(join(f.cwd, '.dsh/skills/custom/SKILL.md'), 'SKILL BODY')
|
|
164
|
-
await assert.rejects(f.call({ operation: 'create', title: 'profile regression', branch: 'main', files: ['app.js'] }), /rule:project:my-rule/)
|
|
165
|
-
f.records.set(join(f.cwd, '.dsh/rules/my-rule.md'), 'RULE BODY')
|
|
166
|
-
await f.call({ operation: 'create', title: 'profile regression', branch: 'main', files: ['app.js'] })
|
|
167
|
-
assert.deepEqual(f.state().flow.config.stage_bindings['开发'].skills[0].rules, [rule])
|
|
168
|
-
assert.deepEqual(f.state().flow.config.stage_bindings['交付'].skills[0].rules, [rule])
|
|
169
|
-
assert.equal(f.state().flow.resources.find(resource => resource.kind === 'rule')?.content, 'RULE BODY')
|
|
170
|
-
assert.equal(JSON.parse(await f.call({ operation: 'status' })).bindings.rules[0].content, 'RULE BODY')
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
test('task creation rejects an unavailable skill or rule before writing a record', async () => {
|
|
174
|
-
const f = fixture()
|
|
175
|
-
f.records.delete(join(f.cwd, '.dsh/task-LIVE-1.json'))
|
|
176
|
-
f.records.set(join(f.cwd, '.dsh/eng.json'), JSON.stringify({ flow: 'minimal', stage_bindings: {
|
|
177
|
-
'开发': { skills: [{ skill: { source: 'project', name: 'missing-skill' }, rules: [] }] },
|
|
178
|
-
} }))
|
|
179
|
-
await assert.rejects(f.call({ operation: 'create', title: 'missing', branch: 'main' }), /unreadable bound resources/)
|
|
180
|
-
assert.equal(f.records.has(join(f.cwd, '.dsh/task-LIVE-1.json')), false)
|
|
181
|
-
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
test('manual skill evidence is approved by the host without a shell command', async () => {
|
|
92
|
+
const config = resolveFlow('minimal', { flow: 'minimal', stage_bindings: {
|
|
93
|
+
'开发': { skills: [{ skill: { source: 'project', name: 'human-check' }, rules: [], evidence: 'manual' }] },
|
|
94
|
+
} }).config
|
|
95
|
+
const approvals = []
|
|
96
|
+
const f = fixture('开发', { flow: { flow: 'minimal', version: 3, config } }, {
|
|
97
|
+
approval: { request: async request => { approvals.push(request); return 'allowed-once' } },
|
|
98
|
+
})
|
|
99
|
+
f.load('human-check')
|
|
100
|
+
await f.call({ operation: 'skill_result', skill_name: 'human-check', evidence: ['checked by owner'] })
|
|
101
|
+
assert.equal(f.runs.length, 0)
|
|
102
|
+
assert.equal(approvals.length, 1)
|
|
103
|
+
assert.equal(f.state().skill_results['开发']['human-check'].approved, true)
|
|
104
|
+
assert.deepEqual(JSON.parse(await f.call({ operation: 'status' })).skill_blockers, [])
|
|
105
|
+
const denied = fixture('开发', { flow: { flow: 'minimal', version: 3, config } }, {
|
|
106
|
+
approval: { request: async () => 'rejected' },
|
|
107
|
+
})
|
|
108
|
+
denied.load('human-check')
|
|
109
|
+
await assert.rejects(denied.call({ operation: 'skill_result', skill_name: 'human-check', evidence: ['claimed approval'] }), /not approved/)
|
|
110
|
+
assert.equal(denied.state().skill_results?.['开发']?.['human-check'], undefined)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('completion rejects unresolved terminal skills and rules', async () => {
|
|
114
|
+
const config = resolveFlow('minimal', { flow: 'minimal', stage_bindings: {
|
|
115
|
+
'交付': { skills: [{ skill: { source: 'project', name: 'must-run' }, rules: [{ source: 'project', name: 'missing' }], evidence: 'command' }] },
|
|
116
|
+
} }).config
|
|
117
|
+
const f = fixture('交付', {
|
|
118
|
+
flow: { flow: 'minimal', version: 3, config },
|
|
119
|
+
items: [{ id: 'A', title: 'small', status: 'done', review: { spec: { outcome: 'pass' }, quality: { outcome: 'pass' } } }],
|
|
120
|
+
commits: [{ label: 'TASK', hash: 'abcdef1' }],
|
|
121
|
+
})
|
|
122
|
+
const status = JSON.parse(await f.call({ operation: 'status' }))
|
|
123
|
+
assert.ok(status.skill_blockers.length > 0)
|
|
124
|
+
assert.ok(status.missing_rules.length > 0)
|
|
125
|
+
await assert.rejects(f.call({ operation: 'complete' }), /cannot complete.*must-run|cannot complete.*missing/)
|
|
126
|
+
assert.equal(f.state().completed, undefined)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
test('same-named skill and rule freeze separately and disclose the rule body', async () => {
|
|
130
|
+
const f = fixture()
|
|
131
|
+
f.records.delete(join(f.cwd, '.dsh/task-LIVE-1.json'))
|
|
132
|
+
f.records.set(join(f.cwd, '.dsh/eng.json'), JSON.stringify({ flow: 'minimal', stage_bindings: {
|
|
133
|
+
'开发': { skills: [{ skill: { source: 'project', name: 'custom' }, rules: [{ source: 'project', name: 'custom' }], evidence: 'none' }] },
|
|
134
|
+
} }))
|
|
135
|
+
f.records.set(join(f.cwd, '.dsh/skills/custom/SKILL.md'), 'SKILL BODY')
|
|
136
|
+
f.records.set(join(f.cwd, '.dsh/rules/custom.md'), 'RULE BODY')
|
|
137
|
+
await f.call({ operation: 'create', title: 'collision', branch: 'main', files: ['app.js'] })
|
|
138
|
+
const resources = f.state().flow.resources.filter(resource => resource.ref.name === 'custom')
|
|
139
|
+
assert.deepEqual(resources.map(resource => resource.kind).sort(), ['rule', 'skill'])
|
|
140
|
+
assert.equal(JSON.parse(await f.call({ operation: 'status' })).bindings.rules[0].content, 'RULE BODY')
|
|
141
|
+
f.records.set(join(f.cwd, '.dsh/skills/custom/SKILL.md'), 'CHANGED SKILL')
|
|
142
|
+
const status = JSON.parse(await f.call({ operation: 'status' }))
|
|
143
|
+
assert.equal(status.bindings.skill_contents[0].content, 'CHANGED SKILL')
|
|
144
|
+
assert.ok(status.changed_source_skills.some(ref => ref.includes('project:custom')))
|
|
145
|
+
f.records.delete(join(f.cwd, '.dsh/skills/custom/SKILL.md'))
|
|
146
|
+
const missing = JSON.parse(await f.call({ operation: 'status' }))
|
|
147
|
+
assert.ok(missing.missing_skills.includes('project:custom'))
|
|
148
|
+
assert.equal(missing.bindings.skill_contents.length, 0)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
test('task creation applies skill_profiles rules even when legacy inline bindings are empty', async () => {
|
|
152
|
+
const f = fixture()
|
|
153
|
+
const skill = { source: 'project', name: 'custom' }
|
|
154
|
+
const rule = { source: 'project', name: 'my-rule' }
|
|
155
|
+
f.records.delete(join(f.cwd, '.dsh/task-LIVE-1.json'))
|
|
156
|
+
f.records.set(join(f.cwd, '.dsh/eng.json'), JSON.stringify({ flow: 'minimal',
|
|
157
|
+
stage_bindings: {
|
|
158
|
+
'开发': { skills: [{ skill, rules: [], evidence: 'none' }] },
|
|
159
|
+
'交付': { skills: [{ skill, rules: [], evidence: 'none' }] },
|
|
160
|
+
},
|
|
161
|
+
skill_profiles: { 'project:custom': { rules: [rule], evidence: 'none' } },
|
|
162
|
+
}))
|
|
163
|
+
f.records.set(join(f.cwd, '.dsh/skills/custom/SKILL.md'), 'SKILL BODY')
|
|
164
|
+
await assert.rejects(f.call({ operation: 'create', title: 'profile regression', branch: 'main', files: ['app.js'] }), /rule:project:my-rule/)
|
|
165
|
+
f.records.set(join(f.cwd, '.dsh/rules/my-rule.md'), 'RULE BODY')
|
|
166
|
+
await f.call({ operation: 'create', title: 'profile regression', branch: 'main', files: ['app.js'] })
|
|
167
|
+
assert.deepEqual(f.state().flow.config.stage_bindings['开发'].skills[0].rules, [rule])
|
|
168
|
+
assert.deepEqual(f.state().flow.config.stage_bindings['交付'].skills[0].rules, [rule])
|
|
169
|
+
assert.equal(f.state().flow.resources.find(resource => resource.kind === 'rule')?.content, 'RULE BODY')
|
|
170
|
+
assert.equal(JSON.parse(await f.call({ operation: 'status' })).bindings.rules[0].content, 'RULE BODY')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('task creation rejects an unavailable skill or rule before writing a record', async () => {
|
|
174
|
+
const f = fixture()
|
|
175
|
+
f.records.delete(join(f.cwd, '.dsh/task-LIVE-1.json'))
|
|
176
|
+
f.records.set(join(f.cwd, '.dsh/eng.json'), JSON.stringify({ flow: 'minimal', stage_bindings: {
|
|
177
|
+
'开发': { skills: [{ skill: { source: 'project', name: 'missing-skill' }, rules: [] }] },
|
|
178
|
+
} }))
|
|
179
|
+
await assert.rejects(f.call({ operation: 'create', title: 'missing', branch: 'main' }), /unreadable bound resources/)
|
|
180
|
+
assert.equal(f.records.has(join(f.cwd, '.dsh/task-LIVE-1.json')), false)
|
|
181
|
+
})
|
|
182
182
|
|
|
183
183
|
test('审查通过后更新状态并激活下一项,保留已完成项的审计', async () => {
|
|
184
184
|
const f = fixture()
|
|
@@ -309,9 +309,9 @@ test('记录字段来自冻结流程,精简流程不硬编码标准字段,
|
|
|
309
309
|
assert.deepEqual(JSON.parse(await fixture('设计').call({ operation: 'status' })).artifact_requirements[0].fields, ['approach', 'risks', 'impact'])
|
|
310
310
|
})
|
|
311
311
|
|
|
312
|
-
test('空绑定流程不会因范围变更产生额外技能门禁', async () => {
|
|
313
|
-
const f = fixture('需求评审', { artifacts: { requirement: { scope: 'device picker', acceptance_criteria: 'contract and selection' } } })
|
|
314
|
-
await f.call({ operation: 'scope', files: ['app.js', 'new-vo.java'] })
|
|
312
|
+
test('空绑定流程不会因范围变更产生额外技能门禁', async () => {
|
|
313
|
+
const f = fixture('需求评审', { artifacts: { requirement: { scope: 'device picker', acceptance_criteria: 'contract and selection' } } })
|
|
314
|
+
await f.call({ operation: 'scope', files: ['app.js', 'new-vo.java'] })
|
|
315
315
|
await f.call({ operation: 'advance', target_stage: '设计' })
|
|
316
316
|
assert.equal(f.state().stage, '设计')
|
|
317
317
|
})
|
|
@@ -411,12 +411,14 @@ test('同名技能与规则在项目和个人目录各自保留管理入口', as
|
|
|
411
411
|
assert.match(read.content, /Run tests/)
|
|
412
412
|
})
|
|
413
413
|
|
|
414
|
-
test('
|
|
414
|
+
test('安装包注册会话编排和元技能,阶段目录只隐藏会话编排技能', async () => {
|
|
415
415
|
const registered = []
|
|
416
416
|
registerShippedSkills({ get: name => name === 'skills' ? { register: skill => { registered.push(skill); return () => {} } } : undefined })
|
|
417
|
-
const expected = ['eng-delivery'
|
|
417
|
+
const expected = ['architecture-design', 'code-development', 'code-review', 'eng-delivery',
|
|
418
|
+
'requirements-analysis', 'task-orchestration', 'test-validation']
|
|
418
419
|
assert.deepEqual(registered.map(skill => skill.name).sort(), expected)
|
|
419
420
|
assert.ok(registered.every(skill => skill.content.trim().length > 0))
|
|
420
421
|
const catalog = await Controller.prototype.listSkills.call({ authorizedPath: async path => path }, '')
|
|
421
|
-
assert.deepEqual(catalog.skills.filter(skill => skill.source === 'bundled').map(skill => skill.name).sort(),
|
|
422
|
-
|
|
422
|
+
assert.deepEqual(catalog.skills.filter(skill => skill.source === 'bundled').map(skill => skill.name).sort(),
|
|
423
|
+
expected.filter(name => name !== 'eng-delivery'))
|
|
424
|
+
})
|
package/README.md
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
<img src="docs/assets/workflow-banner.svg" alt="DSH Task Engine:DeepSeek Harness 的个人工程流程工作台,标准流程从需求评审走向完成" width="100%" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<h1 align="center">DSH Task Engine</h1>
|
|
5
|
+
<h1 align="center">DSH Task Engine</h1>
|
|
6
|
+
|
|
7
|
+
> `0.29.0` 新增自适应流程、项目技能初始化与可选 SonarQube CI 审核。SonarQube 集成目前读取提交后的 CI 分析结果,不提供未提交代码的本地 Sonar 审核。
|
|
6
8
|
|
|
7
|
-
<p align="center"
|
|
8
|
-
<p align="center"><sub>
|
|
9
|
+
<p align="center">按每个需求选择工程路径,用项目 Skill 与 Rule 复用团队开发规范。</p>
|
|
10
|
+
<p align="center"><sub>Task-scoped engineering workflows for DeepSeek Harness.</sub></p>
|
|
9
11
|
|
|
10
12
|
<p align="center">
|
|
11
13
|
<a href="https://www.npmjs.com/package/@godv61/dsh-task-engine"><img src="https://img.shields.io/npm/v/%40godv61%2Fdsh-task-engine?style=flat-square&color=238636" alt="npm version" /></a>
|
|
@@ -24,10 +26,10 @@
|
|
|
24
26
|
|
|
25
27
|
## 为什么用它
|
|
26
28
|
|
|
27
|
-
让 AI
|
|
29
|
+
让 AI 在持续开发的项目中处理不同需求时,按这次需求的复杂度选择低、中、高、超高四档任务流程。元技能规定交接产物,项目 Skill/Rule 承载团队编码规范;同一项目下的多个会话可以各自推进不同任务。
|
|
28
30
|
|
|
29
|
-
-
|
|
30
|
-
- **复用自己的工作方法**:把技能和规则安装到项目或个人目录,将规则配置在技能下,再把技能挂到节点上;同一技能在任何节点都使用同一套约束。
|
|
31
|
+
- **知道下一步做什么**:任务创建时选择复杂度并冻结流程,当前阶段的条件和产物清楚可查。
|
|
32
|
+
- **复用自己的工作方法**:把技能和规则安装到项目或个人目录,将规则配置在技能下,再把技能挂到节点上;同一技能在任何节点都使用同一套约束。
|
|
31
33
|
- **找得到过程记录**:任务台账集中查看阶段、实施项、验证与审核状态。
|
|
32
34
|
|
|
33
35
|
## 快速开始
|
|
@@ -38,37 +40,40 @@
|
|
|
38
40
|
dsh plugin --profile web add @godv61/dsh-task-engine
|
|
39
41
|
```
|
|
40
42
|
|
|
41
|
-
1. 重启 Harness Web,打开侧边栏的
|
|
42
|
-
2. 选择工作区,在
|
|
43
|
+
1. 重启 Harness Web,打开侧边栏的 **工程任务**。
|
|
44
|
+
2. 选择工作区,在 **自适应流程** 中查看四档路径,按需挂载项目技能。
|
|
43
45
|
3. 新建会话,选择 **工程化开发引擎** 预设,描述要完成的开发任务。
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
看到“工程任务”入口和“工程化开发引擎”会话预设,就说明工作台与任务工具已接入。使用 Harness 源码启动的安装方式见[安装与启用](docs/getting-started.md)。
|
|
46
48
|
|
|
47
|
-
>
|
|
49
|
+
> 工程化会话先分析需求复杂度,再以 `dev_task assess` 预览并创建任务;`.dsh/meta.json` 只配置元技能挂载和可选 SonarQube 审核,不强迫项目所有会话走同一条流程。
|
|
48
50
|
|
|
49
51
|
## 工作台里有什么
|
|
50
52
|
|
|
51
53
|
| 页面 | 你可以做什么 |
|
|
52
54
|
| :--- | :--- |
|
|
53
|
-
| **项目初始化** |
|
|
54
|
-
|
|
|
55
|
+
| **项目初始化** | 维护 `AGENTS.md`;工程会话可用 `dev_task init_project` 扫描仓库、预览并生成项目 Skill/Rule。 |
|
|
56
|
+
| **自适应流程** | 查看四档任务路径、元技能绑定与可选 SonarQube 审核配置。 |
|
|
57
|
+
| **旧版流程** | 维护旧项目的 `.dsh/eng.json`;已有任务继续按冻结快照执行。 |
|
|
55
58
|
| **任务台账** | 查看实施、验证和审核记录,按关键词、阶段或风险筛选。 |
|
|
56
|
-
| **技能** | 安装、编辑技能,并集中维护每个技能唯一的规则列表。 |
|
|
59
|
+
| **技能** | 安装、编辑技能,并集中维护每个技能唯一的规则列表。 |
|
|
57
60
|
| **规则** | 选择 Markdown 文件安装规则,维护项目或个人开发约定。 |
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
插件内置会话编排 Skill `eng-delivery` 和六个通用元技能 Skill。项目知识、领域编码方法和 Rule 由使用者创建或通过 init 生成,可放在项目或个人目录;同名项目 Skill 在新任务中覆盖用户级 Skill。
|
|
63
|
+
|
|
64
|
+
新路径的四档顺序、元技能交接契约、`init_project` 和可选 SonarQube 审核见[自适应工程任务](docs/adaptive-workflows.md)。
|
|
60
65
|
|
|
61
|
-
|
|
66
|
+
**流程与工作方法各有职责。** 四档流程规定阶段与门禁,并在相应阶段加载内置元技能;项目业务技能和规则由使用者配置。旧版三个流程不自带技能或提交格式,「采用推荐配置」只填写可修改的提交文本、产物字段和评审深度。同一条规则可由多个技能共享。任务的流程与资源引用在创建时确定;Skill/Rule 正文在每次交互读取最新版本,创建时副本仅用于审计。[新流程说明](docs/adaptive-workflows.md) · [旧版配置细节](docs/configuration.md)
|
|
62
67
|
|
|
63
|
-
##
|
|
68
|
+
## 旧版流程兼容
|
|
64
69
|
|
|
65
70
|
| 预设 | 阶段顺序 | 适用场景 |
|
|
66
71
|
| :--- | :--- | :--- |
|
|
67
|
-
| **完整研发** `standard` | 需求评审 → 设计 → 开发 → 交付 → 代码审核 → 完成 | 需要需求、方案和交付检查的完整开发任务。 |
|
|
68
|
-
| **日常迭代** `agile` | 需求 → 开发 → 交付 → 审查 | 目标明确的日常开发任务。 |
|
|
69
|
-
| **快速修改** `minimal` | 开发 → 交付 | 已明确做法的小改动。 |
|
|
72
|
+
| **完整研发** `standard` | 需求评审 → 设计 → 开发 → 交付 → 代码审核 → 完成 | 需要需求、方案和交付检查的完整开发任务。 |
|
|
73
|
+
| **日常迭代** `agile` | 需求 → 开发 → 交付 → 审查 | 目标明确的日常开发任务。 |
|
|
74
|
+
| **快速修改** `minimal` | 开发 → 交付 | 已明确做法的小改动。 |
|
|
70
75
|
|
|
71
|
-
|
|
76
|
+
旧版阶段顺序和检查条件继续有效;没有传入 `complexity` 的旧调用仍按 `.dsh/eng.json` 执行。新任务通过 `complexity` 使用四档自适应流程;旧任务不迁移或改写。
|
|
72
77
|
|
|
73
78
|
## 把自己的技能和规则带进来
|
|
74
79
|
|
|
@@ -78,11 +83,11 @@ dsh plugin --profile web add @godv61/dsh-task-engine
|
|
|
78
83
|
|
|
79
84
|
| 安装范围 | 存放位置 | 用途 |
|
|
80
85
|
| :--- | :--- | :--- |
|
|
81
|
-
| 项目 | 工作区 `.dsh/skills`、`.dsh/rules` | 当前项目的工作方法与约定;工作台新建资源默认写在这里。 |
|
|
82
|
-
| 个人 | `$DSH_HOME/skills`、`$DSH_HOME/rules` | 在这台电脑上的多个项目间复用。 |
|
|
83
|
-
|
|
84
|
-
工作台也会发现同一项目 `.agents/skills/<名称>/SKILL.md` 中的 Codex 项目技能,可将它绑定到流程节点,并在技能下配置 DSH 规则;它的正文仍留在原目录。运行时使用 `dev_task` 的 `load_skill` 操作读取该技能及其规则,下一次读取会取得文件的最新内容。[Codex 项目技能说明](docs/configuration.md#技能与规则)
|
|
85
|
-
|
|
86
|
+
| 项目 | 工作区 `.dsh/skills`、`.dsh/rules` | 当前项目的工作方法与约定;工作台新建资源默认写在这里。 |
|
|
87
|
+
| 个人 | `$DSH_HOME/skills`、`$DSH_HOME/rules` | 在这台电脑上的多个项目间复用。 |
|
|
88
|
+
|
|
89
|
+
工作台也会发现同一项目 `.agents/skills/<名称>/SKILL.md` 中的 Codex 项目技能,可将它绑定到流程节点,并在技能下配置 DSH 规则;它的正文仍留在原目录。运行时使用 `dev_task` 的 `load_skill` 操作读取该技能及其规则,下一次读取会取得文件的最新内容。[Codex 项目技能说明](docs/configuration.md#技能与规则)
|
|
90
|
+
|
|
86
91
|
技能的脚本、模板和附件会一并保留。文件格式、大小限制和故障处理见[技能与规则安装指南](docs/resource-install.md)。
|
|
87
92
|
|
|
88
93
|
## 使用文档
|
|
@@ -90,7 +95,8 @@ dsh plugin --profile web add @godv61/dsh-task-engine
|
|
|
90
95
|
| 想了解什么 | 从这里开始 |
|
|
91
96
|
| :--- | :--- |
|
|
92
97
|
| 安装、启用与第一次使用 | [快速上手](docs/getting-started.md) |
|
|
93
|
-
|
|
|
98
|
+
| 任务级流程、元技能与 SonarQube | [自适应工程任务](docs/adaptive-workflows.md) |
|
|
99
|
+
| 旧项目配置与阶段绑定 | [流程配置](docs/configuration.md) |
|
|
94
100
|
| 技能文件夹与 Markdown 规则 | [资源安装](docs/resource-install.md) |
|
|
95
101
|
| 完整操作说明 | [HTML 手册](docs/manual.html)(下载后在浏览器打开) |
|
|
96
102
|
| 当前能力与常见问题 | [常见问题](docs/faq.md) |
|
|
@@ -100,7 +106,7 @@ dsh plugin --profile web add @godv61/dsh-task-engine
|
|
|
100
106
|
|
|
101
107
|
## 能力说明
|
|
102
108
|
|
|
103
|
-
`dev_task` 按配置检查阶段、产物和提交条件。新任务的验证要求真实命令回执;技能可按自身配置要求命令、产物、审核、人工批准或无需额外证据,执行义务可在任务状态中查看。任务保存创建时的流程与资源引用;之后修改流程配置不会改变进行中任务的状态机,编辑所引用资源的正文会从下一次交互生效。
|
|
109
|
+
`dev_task` 按配置检查阶段、产物和提交条件。新任务的验证要求真实命令回执;技能可按自身配置要求命令、产物、审核、人工批准或无需额外证据,执行义务可在任务状态中查看。任务保存创建时的流程与资源引用;之后修改流程配置不会改变进行中任务的状态机,编辑所引用资源的正文会从下一次交互生效。
|
|
104
110
|
|
|
105
111
|
审核结论、实施项完成情况和测试覆盖面仍需要你判断。本地提交钩子提供即时检查,不能替代人工审核或项目自己的 CI。详细说明见[常见问题](docs/faq.md)。
|
|
106
112
|
|
package/cordis.patch.yml
CHANGED
|
@@ -1,11 +1,160 @@
|
|
|
1
|
-
# dsh-task-engine bundle:
|
|
2
|
-
#
|
|
3
|
-
# `dsh.client`, the workbench UI. This host row registers NO model-facing tool
|
|
4
|
-
# and NO skill — `dev_task` and the shipped skills live on the AGENT plane,
|
|
5
|
-
# behind the `@godv61/dsh-task-engine/agent` entry. A preset's
|
|
6
|
-
# `agent.cordis.yml` names that agent row to activate the flow for its
|
|
7
|
-
# sessions; presets that never name it get neither the tool nor the skills
|
|
8
|
-
# (the workbench stays available so a human can still configure the flow).
|
|
1
|
+
# dsh-task-engine bundle: mount the host controller and contribute an
|
|
2
|
+
# engineering preset using DSH 0.2's declarative preset plugin.
|
|
9
3
|
- insert:
|
|
10
4
|
- id: task-engine
|
|
11
|
-
name: '@godv61/dsh-task-engine'
|
|
5
|
+
name: '@godv61/dsh-task-engine'
|
|
6
|
+
|
|
7
|
+
# Engineering preset derived from DSH 0.2's shipped standard preset.
|
|
8
|
+
# Keep this declaration in sync with the target DSH release.
|
|
9
|
+
# `config.plugins` by id from the profile patch.
|
|
10
|
+
- insert:
|
|
11
|
+
- id: preset-eng
|
|
12
|
+
name: '@deepseek-ai/dsh-agent-preset'
|
|
13
|
+
config:
|
|
14
|
+
id: eng
|
|
15
|
+
name: 工程化开发引擎
|
|
16
|
+
description: 通过 dev_task 持有工程任务阶段、证据和提交门禁。
|
|
17
|
+
order: 2
|
|
18
|
+
plugins:
|
|
19
|
+
- id: persona
|
|
20
|
+
name: '@deepseek-ai/dsh-persona'
|
|
21
|
+
config:
|
|
22
|
+
prefix: |-
|
|
23
|
+
You are an engineering-delivery coding agent, powered by the {{model}} model, running on DeepSeek Harness. Your working directory is {{cwd}}.
|
|
24
|
+
|
|
25
|
+
本仓库使用 `dev_task` 管理工程任务的状态机。项目根 `.dsh/eng.json` 选择流程骨架,并可配置阶段技能、技能规则、产物字段和提交文本;没有配置的业务方法不能自动补齐。任何开发类请求开始前,先加载 `eng-delivery` 并读取当前任务状态。
|
|
26
|
+
|
|
27
|
+
阶段流转、确认、验证、评审、任务完成及受控提交通过 `dev_task`;它拒绝时按返回的条件修正,不绕过。具体工作方法以当前任务冻结的用户配置为准,远程操作或发布由用户明确决定。
|
|
28
|
+
- id: agent-instructions
|
|
29
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
30
|
+
config:
|
|
31
|
+
maxBytes: 65536
|
|
32
|
+
- id: tool-bash
|
|
33
|
+
name: '@deepseek-ai/dsh-tool-bash'
|
|
34
|
+
disabled: !!js process.platform === 'win32'
|
|
35
|
+
- id: tool-pwsh
|
|
36
|
+
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
37
|
+
disabled: !!js process.platform !== 'win32'
|
|
38
|
+
- id: tool-fs
|
|
39
|
+
name: '@deepseek-ai/dsh-tool-fs'
|
|
40
|
+
- id: tool-fs-search
|
|
41
|
+
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
42
|
+
config:
|
|
43
|
+
sampleOverCapGlobResults: false
|
|
44
|
+
- id: tool-jobs
|
|
45
|
+
name: '@deepseek-ai/dsh-tool-jobs'
|
|
46
|
+
- id: skill-filesystem
|
|
47
|
+
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
48
|
+
- id: tool-skill
|
|
49
|
+
name: '@deepseek-ai/dsh-tool-skill'
|
|
50
|
+
- id: command-goal
|
|
51
|
+
name: '@deepseek-ai/dsh-command-goal'
|
|
52
|
+
- id: tool-goal
|
|
53
|
+
name: '@deepseek-ai/dsh-tool-goal'
|
|
54
|
+
- id: planning
|
|
55
|
+
name: cordis:group
|
|
56
|
+
group: true
|
|
57
|
+
isolate:
|
|
58
|
+
planMode: true
|
|
59
|
+
config:
|
|
60
|
+
- id: plan-mode
|
|
61
|
+
name: '@deepseek-ai/dsh-plan-mode'
|
|
62
|
+
config:
|
|
63
|
+
section: |
|
|
64
|
+
You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
|
|
65
|
+
|
|
66
|
+
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
67
|
+
|
|
68
|
+
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
|
|
69
|
+
|
|
70
|
+
Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
|
|
71
|
+
|
|
72
|
+
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
|
|
73
|
+
|
|
74
|
+
When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
|
|
75
|
+
- id: compaction
|
|
76
|
+
name: cordis:group
|
|
77
|
+
group: true
|
|
78
|
+
isolate:
|
|
79
|
+
compaction: true
|
|
80
|
+
toolResultPruner: true
|
|
81
|
+
config:
|
|
82
|
+
- id: compaction-basic
|
|
83
|
+
name: '@deepseek-ai/dsh-compaction-basic'
|
|
84
|
+
- id: command-compact
|
|
85
|
+
name: '@deepseek-ai/dsh-command-compact'
|
|
86
|
+
- id: tool-result-pruner
|
|
87
|
+
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
88
|
+
config:
|
|
89
|
+
thresholdChars: 8192
|
|
90
|
+
headChars: 4096
|
|
91
|
+
tailChars: 1024
|
|
92
|
+
- id: delegation
|
|
93
|
+
name: cordis:group
|
|
94
|
+
group: true
|
|
95
|
+
isolate:
|
|
96
|
+
workflowEngine: true
|
|
97
|
+
config:
|
|
98
|
+
- id: tool-subagent-control
|
|
99
|
+
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
100
|
+
- id: tool-subagent-list-agents
|
|
101
|
+
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
102
|
+
- id: tool-subagent
|
|
103
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
104
|
+
config:
|
|
105
|
+
provider: spawn
|
|
106
|
+
toolName: subagent
|
|
107
|
+
modelSelectionSettings: true
|
|
108
|
+
backgroundMode: continuable
|
|
109
|
+
- id: tool-subagent-fork
|
|
110
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
111
|
+
config:
|
|
112
|
+
provider: fork
|
|
113
|
+
toolName: subagent_fork
|
|
114
|
+
backgroundMode: continuable
|
|
115
|
+
- id: tool-subagent-codex
|
|
116
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
117
|
+
disabled: true
|
|
118
|
+
config:
|
|
119
|
+
provider: codex
|
|
120
|
+
toolName: subagent_codex
|
|
121
|
+
backgroundMode: one-shot
|
|
122
|
+
maxDepth: provider-managed
|
|
123
|
+
- id: tool-subagent-claude-code
|
|
124
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
125
|
+
disabled: true
|
|
126
|
+
config:
|
|
127
|
+
provider: claude-code
|
|
128
|
+
toolName: subagent_claude_code
|
|
129
|
+
backgroundMode: one-shot
|
|
130
|
+
maxDepth: provider-managed
|
|
131
|
+
- id: workflow-ptc
|
|
132
|
+
name: '@deepseek-ai/dsh-workflow-ptc'
|
|
133
|
+
config:
|
|
134
|
+
provider: spawn
|
|
135
|
+
- id: tool-workflow
|
|
136
|
+
name: '@deepseek-ai/dsh-tool-workflow'
|
|
137
|
+
- id: tool-ralph
|
|
138
|
+
name: '@deepseek-ai/dsh-tool-ralph'
|
|
139
|
+
disabled: true
|
|
140
|
+
config:
|
|
141
|
+
subagentProvider: spawn
|
|
142
|
+
maxRounds: 64
|
|
143
|
+
- id: tool-ask-user
|
|
144
|
+
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
145
|
+
- id: tool-todo
|
|
146
|
+
name: '@deepseek-ai/dsh-tool-todo'
|
|
147
|
+
config:
|
|
148
|
+
allowParallelInProgress: true
|
|
149
|
+
- id: tool-web
|
|
150
|
+
name: '@deepseek-ai/dsh-tool-web'
|
|
151
|
+
config:
|
|
152
|
+
fetch: true
|
|
153
|
+
searchTimeoutMs: 60000
|
|
154
|
+
- id: present
|
|
155
|
+
name: '@deepseek-ai/dsh-tool-present'
|
|
156
|
+
- id: tool-plugin-manager
|
|
157
|
+
name: '@deepseek-ai/dsh-plugin-manager/tools'
|
|
158
|
+
disabled: true
|
|
159
|
+
- id: task-engine-agent
|
|
160
|
+
name: '@godv61/dsh-task-engine/agent'
|