@cat-kit/agent-context 1.0.3

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.
Files changed (52) hide show
  1. package/dist/adapters/tool-targets.d.ts +18 -0
  2. package/dist/adapters/tool-targets.js +2 -0
  3. package/dist/adapters/tool-targets.js.map +1 -0
  4. package/dist/domain/types.d.ts +37 -0
  5. package/dist/domain/workflow-content.d.ts +7 -0
  6. package/dist/domain/workflow-content.js +2 -0
  7. package/dist/domain/workflow-content.js.map +1 -0
  8. package/dist/domain/workflow-context.js +2 -0
  9. package/dist/domain/workflow-context.js.map +1 -0
  10. package/dist/domain/workflow-templates/done.js +23 -0
  11. package/dist/domain/workflow-templates/done.js.map +1 -0
  12. package/dist/domain/workflow-templates/implement.js +35 -0
  13. package/dist/domain/workflow-templates/implement.js.map +1 -0
  14. package/dist/domain/workflow-templates/init.js +37 -0
  15. package/dist/domain/workflow-templates/init.js.map +1 -0
  16. package/dist/domain/workflow-templates/patch.js +41 -0
  17. package/dist/domain/workflow-templates/patch.js.map +1 -0
  18. package/dist/domain/workflow-templates/plan.js +58 -0
  19. package/dist/domain/workflow-templates/plan.js.map +1 -0
  20. package/dist/domain/workflow-templates/replan.js +31 -0
  21. package/dist/domain/workflow-templates/replan.js.map +1 -0
  22. package/dist/generators/workflow.js +2 -0
  23. package/dist/generators/workflow.js.map +1 -0
  24. package/dist/index.d.ts +5 -0
  25. package/dist/index.js +1 -0
  26. package/dist/runtime/execute.d.ts +8 -0
  27. package/dist/runtime/execute.js +2 -0
  28. package/dist/runtime/execute.js.map +1 -0
  29. package/dist/shared/fs.js +3 -0
  30. package/dist/shared/fs.js.map +1 -0
  31. package/dist/stats.html +4950 -0
  32. package/package.json +32 -0
  33. package/src/adapters/tool-targets.ts +157 -0
  34. package/src/cli.ts +32 -0
  35. package/src/commands/setup.ts +64 -0
  36. package/src/commands/shared.ts +38 -0
  37. package/src/commands/update.ts +46 -0
  38. package/src/domain/types.ts +50 -0
  39. package/src/domain/workflow-content.ts +25 -0
  40. package/src/domain/workflow-context.ts +48 -0
  41. package/src/domain/workflow-templates/done.ts +28 -0
  42. package/src/domain/workflow-templates/implement.ts +41 -0
  43. package/src/domain/workflow-templates/index.ts +7 -0
  44. package/src/domain/workflow-templates/init.ts +42 -0
  45. package/src/domain/workflow-templates/patch.ts +47 -0
  46. package/src/domain/workflow-templates/plan.ts +70 -0
  47. package/src/domain/workflow-templates/replan.ts +36 -0
  48. package/src/generators/workflow.ts +25 -0
  49. package/src/index.ts +11 -0
  50. package/src/runtime/execute.ts +45 -0
  51. package/src/shared/fs.ts +66 -0
  52. package/src/shared/paths.ts +9 -0
@@ -0,0 +1,70 @@
1
+ import {
2
+ code,
3
+ fence,
4
+ renderNextSteps,
5
+ renderPreamble,
6
+ type WorkflowContext
7
+ } from '../workflow-context'
8
+
9
+ export function renderPlan(c: WorkflowContext): string {
10
+ return `${c.frontmatter('创建新计划并维护单当前计划 + preparing 队列结构')}\
11
+ # ${c.invoke('plan')} {描述}
12
+
13
+ ${renderPreamble(c, 'plan', `创建新的执行计划,写入 ${code('.agent-context/plan-{number}/plan.md')}。按复杂度可拆分为多个计划,维护「单当前计划 + preparing 队列」结构。`)}
14
+
15
+ ## 前置规则
16
+
17
+ - 描述为空 → 拒绝执行,提示必须附带描述。
18
+ - 存在未归档的已执行当前计划 → 拒绝执行,提示先运行 ${code(c.cmd('done'))}。
19
+ - 存在多个当前计划 → 拒绝执行,提示恢复单活跃状态。
20
+ - 编号:扫描 ${code('.agent-context/{,preparing/,done/}')} 全部 ${code('plan-N')},取 max(N)+1,不复用。
21
+
22
+ ## 执行步骤
23
+
24
+ 1. **需求澄清**(存在以下任一歧义时必须向用户提问,否则直接跳到步骤 2):
25
+ - 范围边界不清:无法判定影响哪些文件或模块。
26
+ - 存在显著不同的技术路径需用户决策。
27
+ - 验收标准不明确:无法判断何时算"完成"。
28
+ 2. 按复杂度决定单计划或多计划拆分。
29
+ 3. 多计划拆分时:最小编号进入 ${code('.agent-context/')} 作为当前计划,其余进入 ${code('.agent-context/preparing/')}。单计划直接创建到 ${code('.agent-context/plan-{number}')}。
30
+ 4. 每个计划创建 ${code('plan.md')},遵循下方标准模板。
31
+ 5. **计划自检**(不通过则修改后重新检查):
32
+ - 每个步骤可独立执行且有明确完成标准。
33
+ - 不存在过度拆分(过于琐碎)或拆分不足(单步骤过于复杂)。
34
+ - 影响范围可预估。
35
+
36
+ ## plan.md 标准模板
37
+
38
+ ${fence}markdown
39
+ # {计划名称}
40
+
41
+ > 状态: 未执行
42
+
43
+ ## 目标
44
+
45
+ {明确的目标描述}
46
+
47
+ ## 内容
48
+
49
+ {详细的实施步骤和内容}
50
+
51
+ ## 影响范围
52
+
53
+ - 新增文件: \`/path/to/file\`
54
+ - 修改文件: \`/path/to/file\`
55
+ - 删除文件: \`/path/to/file\`
56
+
57
+ ## 历史补丁
58
+
59
+ - patch-1: {补丁名称}
60
+ ${fence}
61
+
62
+ - 状态行唯一,仅允许 ${code('未执行')} 或 ${code('已执行')}。
63
+ - ${code('## 目标')} 与 ${code('## 内容')} 不可为空。
64
+ - ${code('## 影响范围')} 与 ${code('## 历史补丁')} 创建时留空,分别由 ${code(c.cmd('implement'))} 和 ${code(c.cmd('patch'))} 写入。
65
+
66
+ ${renderNextSteps(c, [
67
+ { command: 'implement', description: '实施当前计划' },
68
+ { command: 'replan', description: '调整计划' }
69
+ ])}`
70
+ }
@@ -0,0 +1,36 @@
1
+ import { code, renderNextSteps, renderPreamble, type WorkflowContext } from '../workflow-context'
2
+
3
+ export function renderReplan(c: WorkflowContext): string {
4
+ return `${c.frontmatter('重规划未实施计划,保持单当前计划结构不变')}\
5
+ # ${c.invoke('replan')} {描述}
6
+
7
+ ${renderPreamble(c, 'replan', `重新规划已有的未实施计划(${code('preparing/')} 队列或未执行的当前计划),保持单当前计划结构不变。`)}
8
+
9
+ ## 前置规则
10
+
11
+ - 描述为空 → 拒绝执行。
12
+ - 无未实施计划 → 拒绝执行,提示使用 ${code(c.cmd('plan'))} 创建。
13
+ - 存在多个当前计划 → 拒绝执行,提示恢复单活跃状态。
14
+ - 目标计划编号不存在 → 拒绝执行,列出可选编号。
15
+ - 目标计划已执行 → 拒绝执行,提示使用 ${code(c.cmd('patch'))} 修补。
16
+ - 重规划后违反单当前计划约束 → 回滚并报错。
17
+
18
+ ## 作用域规则
19
+
20
+ - 默认作用域:${code('.agent-context/preparing/')} 中全部未实施计划。
21
+ - 可通过描述指定仅重规划部分计划(如"重规划 plan-3 和 plan-5")。
22
+ - 当前计划为 ${code('已执行')} → 禁止重写,仅允许重规划 ${code('preparing/')} 队列。
23
+ - 当前计划为 ${code('未执行')} 且用户明确要求 → 可纳入重规划范围。
24
+
25
+ ## 执行步骤
26
+
27
+ 1. 解析描述,确定重规划目标范围。
28
+ 2. 读取目标计划 ${code('plan.md')},理解现有意图。
29
+ 3. 生成新的拆分方案,保持"单当前计划 + 若干 preparing 计划"结构。
30
+ 4. 新增计划编号:全局 max(N)+1 递增分配;未改动计划保持原编号。
31
+ 5. 更新目录结构,确保每个新计划的 ${code('plan.md')} 遵循标准模板。
32
+
33
+ ${renderNextSteps(c, [
34
+ { command: 'implement', description: '实施当前计划' }
35
+ ])}`
36
+ }
@@ -0,0 +1,25 @@
1
+ import { resolveWorkflowPaths } from '../adapters/tool-targets'
2
+ import type { FileMutation, ToolTarget, WorkflowCommandName } from '../domain/types'
3
+ import { renderWorkflowArtifacts } from '../domain/workflow-content'
4
+
5
+ const COMMAND_ORDER: WorkflowCommandName[] = [
6
+ 'init',
7
+ 'plan',
8
+ 'replan',
9
+ 'implement',
10
+ 'patch',
11
+ 'done'
12
+ ]
13
+
14
+ export function renderWorkflowMutations(target: ToolTarget, cwd: string): FileMutation[] {
15
+ const artifacts = renderWorkflowArtifacts(target)
16
+ const paths = resolveWorkflowPaths(target, cwd)
17
+
18
+ const mutations: FileMutation[] = []
19
+
20
+ for (const command of COMMAND_ORDER) {
21
+ mutations.push({ path: paths.commandFile(command), body: artifacts.commandFiles[command] })
22
+ }
23
+
24
+ return mutations
25
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ export { resolveToolTargets, parseToolIds, resolveWorkflowPaths } from './adapters/tool-targets'
2
+ export { renderWorkflowArtifacts } from './domain/workflow-content'
3
+ export { runSetup, runUpdate } from './runtime/execute'
4
+ export type {
5
+ ToolId,
6
+ ToolTarget,
7
+ RunOptions,
8
+ RunResult,
9
+ WorkflowArtifacts,
10
+ WorkflowCommandName
11
+ } from './domain/types.js'
@@ -0,0 +1,45 @@
1
+ import { resolveToolTargets } from '../adapters/tool-targets'
2
+ import type { FileMutation, RunOptions, RunResult, ToolId } from '../domain/types'
3
+ import { renderWorkflowMutations } from '../generators/workflow'
4
+ import { applyManagedMutations } from '../shared/fs'
5
+
6
+ export async function runSetup(options: RunOptions = {}): Promise<RunResult> {
7
+ return runInMode('setup', options)
8
+ }
9
+
10
+ export async function runUpdate(options: RunOptions = {}): Promise<RunResult> {
11
+ return runInMode('update', options)
12
+ }
13
+
14
+ async function runInMode(mode: 'setup' | 'update', options: RunOptions): Promise<RunResult> {
15
+ const cwd = options.cwd ?? process.cwd()
16
+ const toolIds = normalizeTools(options.tools)
17
+ const targets = resolveToolTargets(toolIds)
18
+
19
+ const mutations: FileMutation[] = targets.flatMap(target => renderWorkflowMutations(target, cwd))
20
+
21
+ const check = options.check ?? false
22
+ const result = await applyManagedMutations(mutations, check)
23
+
24
+ return {
25
+ ...result,
26
+ mode,
27
+ check
28
+ }
29
+ }
30
+
31
+ function normalizeTools(tools?: ToolId[]): ToolId[] | undefined {
32
+ if (!tools || tools.length === 0) {
33
+ return undefined
34
+ }
35
+
36
+ const uniqueTools: ToolId[] = []
37
+
38
+ for (const tool of tools) {
39
+ if (!uniqueTools.includes(tool)) {
40
+ uniqueTools.push(tool)
41
+ }
42
+ }
43
+
44
+ return uniqueTools
45
+ }
@@ -0,0 +1,66 @@
1
+ import { dirname } from 'node:path'
2
+ import { existsSync } from 'node:fs'
3
+ import { mkdir, readFile, writeFile } from 'node:fs/promises'
4
+
5
+ import type { ApplyMutationResult, FileMutation } from '../domain/types'
6
+
7
+ export async function applyManagedMutations(
8
+ mutations: FileMutation[],
9
+ check: boolean
10
+ ): Promise<ApplyMutationResult> {
11
+ const result: ApplyMutationResult = {
12
+ created: [],
13
+ updated: [],
14
+ unchanged: [],
15
+ changed: []
16
+ }
17
+
18
+ for (const mutation of mutations) {
19
+ const fileExists = existsSync(mutation.path)
20
+ const nextContent = await resolveNextContent(mutation, fileExists)
21
+
22
+ if (!nextContent.changed) {
23
+ result.unchanged.push(mutation.path)
24
+ continue
25
+ }
26
+
27
+ if (!check) {
28
+ await mkdir(dirname(mutation.path), { recursive: true })
29
+ await writeFile(mutation.path, nextContent.content, 'utf-8')
30
+ }
31
+
32
+ result.changed.push(mutation.path)
33
+
34
+ if (fileExists) {
35
+ result.updated.push(mutation.path)
36
+ } else {
37
+ result.created.push(mutation.path)
38
+ }
39
+ }
40
+
41
+ return result
42
+ }
43
+
44
+ async function resolveNextContent(
45
+ mutation: FileMutation,
46
+ fileExists: boolean
47
+ ): Promise<{ content: string; changed: boolean }> {
48
+ const nextContent = normalizeTrailingNewline(mutation.body)
49
+
50
+ if (!fileExists) {
51
+ return {
52
+ content: nextContent,
53
+ changed: true
54
+ }
55
+ }
56
+
57
+ const currentContent = await readFile(mutation.path, 'utf-8')
58
+ return {
59
+ content: nextContent,
60
+ changed: currentContent !== nextContent
61
+ }
62
+ }
63
+
64
+ function normalizeTrailingNewline(content: string): string {
65
+ return content.endsWith('\n') ? content : `${content}\n`
66
+ }
@@ -0,0 +1,9 @@
1
+ import { dirname, resolve } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+
4
+ const __filename = fileURLToPath(import.meta.url)
5
+ const __dirname = dirname(__filename)
6
+
7
+ export function getPackageRootDir(): string {
8
+ return resolve(__dirname, '../..')
9
+ }