Files
bilin/docs/superpowers/plans/2026-07-07-bilin-p6-ai-context.md
bing aa2c7dfed3 feat: ship v0.7.0 with AI knowledge context integration
Add semi-automatic knowledge suggestions and user-confirmed injection across chat, interactive, auto, and wizard modes; fix writing flows to persist full systemPrompt in contextJson.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 15:33:53 +08:00

28 KiB
Raw Permalink Blame History

笔临 P6 AI 知识上下文整合 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [x]) syntax for tracking.

Goal: 交付 v0.7.0:已审核知识库条目半自动推荐 + 用户勾选确认 + 注入对话/交互/自动/向导四种 AI 模式;修复写作流 contextJson 仅存标题摘要的问题。

Architecture: 新增 KnowledgeRetrievalServiceapproved 知识条目综合打分;扩展 AiContextBinding.knowledgeIdsAiContextBuilderService 合并知识块;FlowContextPayload 统一写作流上下文存储;KnowledgeSuggestList + ContextEditorModal 知识 Tab 供四种模式共用。测试 禁止 Mock AI 集成/E2E 打真实 LM Studio。

Tech Stack: Electron 36、electron-vite、React 18、TypeScript、node:sqlite、Zustand、i18next、Vitest、Playwright

Spec: docs/superpowers/specs/2026-07-07-bilin-p6-ai-context-design.md

LM Studio 默认: baseUrl=http://127.0.0.1:1234model=gemma-4-e4b-it


File Map

文件 职责
src/shared/types.ts KnowledgeSuggestOptionsScoredKnowledgeFlowContextPayload;扩展 AiContextBinding
src/shared/ipc-channels.ts KNOWLEDGE_SUGGEST_CONTEXT
src/main/services/knowledge-retrieval.service.ts 打分 + TopN 推荐
src/main/services/ai-context-builder.service.ts knowledge 块 + 预算裁剪
src/main/services/flow-context.util.ts buildFlowContextPayload() 共享
src/main/services/interactive-writing.service.ts readContextTextsystemPrompt
src/main/db/repositories/ai-session.repo.ts parseContextknowledgeIds
src/main/ipc/handlers/knowledge.handler.ts suggestContext
src/main/ipc/handlers/interactive.handler.ts FlowContextPayload
src/main/ipc/handlers/auto.handler.ts readContextText 修复
src/main/ipc/handlers/wizard.handler.ts 同上 + preview 知识列表
src/main/ipc/handlers/ai.handler.ts binding 默认 knowledgeIds: []
src/main/services/global-settings.ts knowledgeAutoSuggest / knowledgeSuggestTopN 默认值
src/preload/index.ts knowledge.suggestContext
src/shared/electron-api.d.ts 类型
src/renderer/components/ai/KnowledgeSuggestList.tsx 共享勾选 UI
src/renderer/components/ai/ContextEditorModal.tsx 知识 Tab + 推荐区
src/renderer/components/ai/InteractivePanel.tsx context_confirm 知识区
src/renderer/components/ai/AutoPanel.tsx goal 后知识确认
src/renderer/components/ai/WizardPanel.tsx wizard_context 知识区
src/renderer/lib/ai-context-summary.ts 摘要含知识计数
src/renderer/stores/useInteractiveStore.ts binding 含 knowledgeIds
public/locales/zh-CN/translation.json P6 文案
public/locales/en/translation.json P6 文案
tests/main/knowledge-retrieval.test.ts UT-CTX-01/04
tests/main/ai-context-builder.test.ts UT-CTX-02/03(扩展)
tests/main/flow-context.test.ts FlowContextPayload 单元
tests/main/ai-context-integration.test.ts IT-CTX-01/02/03
e2e/ai-context.spec.ts E2E-CTX-*

Task 1: 类型 + IPC 通道 + 默认 binding

Files:

  • Modify: src/shared/types.ts

  • Modify: src/shared/ipc-channels.ts

  • Modify: src/main/db/repositories/ai-session.repo.ts

  • Step 1: 扩展 types

src/shared/types.ts 追加:

export interface KnowledgeSuggestOptions {
  currentChapterId?: string
  goalText?: string
  topN?: number
}

export interface ScoredKnowledge {
  entry: KnowledgeEntry
  score: number
  reasons: string[]
}

export interface FlowContextPayload {
  binding: AiContextBinding
  systemPrompt: string
  contextSummary: string
}

扩展 AiContextBinding

export interface AiContextBinding {
  chapterIds: string[]
  outlineIds: string[]
  settingIds: string[]
  inspirationIds: string[]
  knowledgeIds: string[]
}

扩展 ContextPreviewKind| 'knowledge'

扩展 ContextPreviewItemknowledgeType?: KnowledgeType

扩展 GlobalSettings(可选,本 Task 仅类型,Task 2 实现默认值):

knowledgeAutoSuggest: boolean
knowledgeSuggestTopN: number
  • Step 2: IPC 通道

src/shared/ipc-channels.ts

KNOWLEDGE_SUGGEST_CONTEXT: 'knowledge:suggestContext',
  • Step 3: ai-session.repo 默认 knowledgeIds
const EMPTY_CONTEXT: AiContextBinding = {
  chapterIds: [],
  outlineIds: [],
  settingIds: [],
  inspirationIds: [],
  knowledgeIds: []
}

function parseContext(json: string): AiContextBinding {
  try {
    const parsed = JSON.parse(json) as Partial<AiContextBinding>
    return {
      chapterIds: parsed.chapterIds ?? [],
      outlineIds: parsed.outlineIds ?? [],
      settingIds: parsed.settingIds ?? [],
      inspirationIds: parsed.inspirationIds ?? [],
      knowledgeIds: parsed.knowledgeIds ?? []
    }
  } catch {
    return { ...EMPTY_CONTEXT }
  }
}
  • Step 4: 全局修补 EMPTY_BINDING

搜索 inspirationIds: [] 且无 knowledgeIdsAiContextBinding 字面量(ContextEditorModal.tsxuseInteractiveStore.ts 等),全部补上 knowledgeIds: []

  • Step 5: build 验证
npm run build
  • Step 6: Commit
git add src/shared/types.ts src/shared/ipc-channels.ts src/main/db/repositories/ai-session.repo.ts
git commit -m "feat: extend AiContextBinding with knowledgeIds for P6"

Task 2: KnowledgeRetrievalService

Files:

  • Create: src/main/services/knowledge-retrieval.service.ts

  • Create: tests/main/knowledge-retrieval.test.ts

  • Step 1: UT-CTX-01 / UT-CTX-04 失败测试

tests/main/knowledge-retrieval.test.ts

import { describe, it, expect, afterEach } from 'vitest'
import { DatabaseSync } from 'node:sqlite'
import { migrate } from '../../src/main/db/migrate'
import { VolumeRepository } from '../../src/main/db/repositories/volume.repo'
import { ChapterRepository } from '../../src/main/db/repositories/chapter.repo'
import { KnowledgeRepository } from '../../src/main/db/repositories/knowledge.repo'
import { KnowledgeRetrievalService } from '../../src/main/services/knowledge-retrieval.service'
import type { SqliteDb } from '../../src/main/db/types'

describe('KnowledgeRetrievalService', () => {
  let db: SqliteDb
  afterEach(() => db?.close())

  it('UT-CTX-01: ranks by importance + chapter proximity + goal match', () => {
    db = new DatabaseSync(':memory:')
    migrate(db)
    const volId = new VolumeRepository(db).create('卷一', 0).id
    const chapterRepo = new ChapterRepository(db)
    const cur = chapterRepo.create(volId, '当前章', 5, '<p>x</p>')
    const near = chapterRepo.create(volId, '近章', 4, '<p>x</p>')
    chapterRepo.create(volId, '远章', 0, '<p>x</p>')
    const repo = new KnowledgeRepository(db)
    const low = repo.create({
      type: 'fact',
      title: '无关事实',
      content: '普通内容',
      importance: 2,
      status: 'approved'
    })
    const high = repo.create({
      type: 'foreshadow',
      title: '测灵石异常',
      content: '测灵石在入门考核中发热',
      importance: 5,
      status: 'approved',
      foreshadowStatus: 'buried',
      sourceChapterId: near.id
    })
    const service = new KnowledgeRetrievalService(db)
    const result = service.suggest({
      currentChapterId: cur.id,
      goalText: '测灵石入门考核',
      topN: 5
    })
    expect(result[0].entry.id).toBe(high.id)
    expect(result[0].score).toBeGreaterThan(
      result.find((r) => r.entry.id === low.id)!.score
    )
    expect(result[0].reasons.length).toBeGreaterThan(0)
  })

  it('UT-CTX-04: resolved foreshadow scores lower than buried', () => {
    db = new DatabaseSync(':memory:')
    migrate(db)
    const volId = new VolumeRepository(db).create('卷一', 0).id
    const chId = new ChapterRepository(db).create(volId, '章', 0).id
    const repo = new KnowledgeRepository(db)
    const buried = repo.update(
      repo.create({
        type: 'foreshadow',
        title: '埋设伏笔',
        importance: 3,
        status: 'approved',
        foreshadowStatus: 'buried',
        sourceChapterId: chId
      }).id,
      { status: 'approved' }
    )
    const resolved = repo.create({
      type: 'foreshadow',
      title: '已回收伏笔',
      importance: 3,
      status: 'approved',
      foreshadowStatus: 'resolved',
      sourceChapterId: chId
    })
    const service = new KnowledgeRetrievalService(db)
    const scores = service.suggest({ currentChapterId: chId, topN: 10 })
    const buriedScore = scores.find((s) => s.entry.id === buried.id)!.score
    const resolvedScore = scores.find((s) => s.entry.id === resolved.id)!.score
    expect(buriedScore).toBeGreaterThan(resolvedScore)
  })
})
  • Step 2: 运行确认 FAIL
npm run test -- tests/main/knowledge-retrieval.test.ts
  • Step 3: 实现 KnowledgeRetrievalService

src/main/services/knowledge-retrieval.service.ts 核心:

import type { KnowledgeEntry, KnowledgeSuggestOptions, ScoredKnowledge } from '../../shared/types'
import type { SqliteDb } from '../db/types'
import { ChapterRepository } from '../db/repositories/chapter.repo'
import { KnowledgeRepository } from '../db/repositories/knowledge.repo'

const STOP_WORDS = new Set(['的', '了', '在', '是', '与', '和', 'the', 'a', 'an'])

function tokenize(text: string): string[] {
  return text
    .toLowerCase()
    .split(/[\s,,。!?、;;:]+/)
    .map((t) => t.trim())
    .filter((t) => t.length >= 2 && !STOP_WORDS.has(t))
}

function overlapRatio(a: string, b: string): number {
  const ta = new Set(tokenize(a))
  const tb = tokenize(b)
  if (ta.size === 0 || tb.length === 0) return 0
  let hit = 0
  for (const t of tb) if (ta.has(t)) hit++
  return hit / tb.length
}

function chapterProximityBonus(
  entry: KnowledgeEntry,
  currentSort: number | null,
  chapterSort: Map<string, number>
): number {
  if (currentSort == null) return 0
  const ids = [entry.sourceChapterId, entry.lastMentionChapterId].filter(Boolean) as string[]
  if (ids.length === 0) return 0
  let best = Infinity
  for (const id of ids) {
    const order = chapterSort.get(id)
    if (order != null) best = Math.min(best, Math.abs(currentSort - order))
  }
  if (best === Infinity) return 0
  if (best === 0) return 30
  if (best <= 3) return 20
  if (best <= 10) return 10
  return 0
}

export class KnowledgeRetrievalService {
  constructor(private db: SqliteDb) {}

  suggest(opts: KnowledgeSuggestOptions = {}): ScoredKnowledge[] {
    const topN = opts.topN ?? 8
    const entries = new KnowledgeRepository(this.db).list({ status: 'approved' })
    if (entries.length === 0) return []

    const chapters = new ChapterRepository(this.db).list()
    const sortMap = new Map(chapters.map((c) => [c.id, c.sortOrder]))
    const currentSort = opts.currentChapterId
      ? (sortMap.get(opts.currentChapterId) ?? null)
      : null
    const goalText = opts.goalText ?? ''

    const scored: ScoredKnowledge[] = entries.map((entry) => {
      let score = entry.importance * 20
      const reasons: string[] = []

      const prox = chapterProximityBonus(entry, currentSort, sortMap)
      score += prox
      if (prox > 0) reasons.push('knowledge.reason.chapterRelevant')

      if (entry.type === 'foreshadow') {
        const fs = entry.foreshadowStatus ?? 'buried'
        if (fs === 'buried') {
          score += 25
          reasons.push('knowledge.reason.foreshadowBuried')
        } else if (fs === 'partial') score += 15
      }

      if (goalText) {
        const text = `${entry.title} ${entry.content}`
        const ratio = overlapRatio(goalText, text)
        score += Math.round(ratio * 30)
        if (ratio > 0.2) reasons.push('knowledge.reason.goalMatch')
      }

      if (entry.importance >= 4) reasons.push('knowledge.reason.highImportance')

      return { entry, score, reasons: [...new Set(reasons)] }
    })

    return scored.sort((a, b) => b.score - a.score).slice(0, topN)
  }
}
  • Step 4: 运行 PASS
npm run test -- tests/main/knowledge-retrieval.test.ts
  • Step 5: Commit
git add src/main/services/knowledge-retrieval.service.ts tests/main/knowledge-retrieval.test.ts
git commit -m "feat: add KnowledgeRetrievalService with scoring for context suggest"

Task 3: 扩展 AiContextBuilder + flow-context 工具

Files:

  • Modify: src/main/services/ai-context-builder.service.ts

  • Create: src/main/services/flow-context.util.ts

  • Modify: tests/main/ai-context-builder.test.ts

  • Create: tests/main/flow-context.test.ts

  • Step 1: UT-CTX-02 / UT-CTX-03 测试

tests/main/ai-context-builder.test.ts 追加:

import { KnowledgeRepository } from '../../src/main/db/repositories/knowledge.repo'

it('UT-CTX-02: build includes knowledge preview items', () => {
  const know = new KnowledgeRepository(db)
  const entry = know.create({
    type: 'foreshadow',
    title: '测灵石',
    content: '石头发热',
    status: 'approved'
  })
  const result = aiContextBuilder.build(
    { chapterIds: [], outlineIds: [], settingIds: [], inspirationIds: [], knowledgeIds: [entry.id] },
    registry,
    bookId,
    '作者'
  )
  expect(result.preview.some((p) => p.kind === 'knowledge')).toBe(true)
  expect(result.systemPrompt).toContain('【知识·伏笔】')
  expect(result.systemPrompt).toContain('测灵石')
})

it('UT-CTX-03: caps knowledge section at 4000 chars', () => {
  const know = new KnowledgeRepository(db)
  const ids: string[] = []
  for (let i = 0; i < 20; i++) {
    const e = know.create({
      type: 'fact',
      title: `条目${i}`,
      content: '长'.repeat(400),
      importance: 5,
      status: 'approved'
    })
    ids.push(e.id)
  }
  const result = aiContextBuilder.build(
    { chapterIds: [], outlineIds: [], settingIds: [], inspirationIds: [], knowledgeIds: ids },
    registry,
    bookId,
    '作者'
  )
  const knowledgeChars = result.preview
    .filter((p) => p.kind === 'knowledge')
    .reduce((s, p) => s + p.charCount, 0)
  expect(knowledgeChars).toBeLessThanOrEqual(4000)
})

tests/main/flow-context.test.ts

import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { mkdtempSync, rmSync } from 'fs'
import { join } from 'path'
import { tmpdir } from 'os'
import { BookRegistryService } from '../../src/main/services/book-registry'
import { buildFlowContextPayload } from '../../src/main/services/flow-context.util'
import { KnowledgeRepository } from '../../src/main/db/repositories/knowledge.repo'

describe('flow-context.util', () => {
  let userDir: string
  let registry: BookRegistryService
  let bookId: string

  beforeEach(() => {
    userDir = mkdtempSync(join(tmpdir(), 'bilin-flow-ctx-'))
    registry = new BookRegistryService(userDir)
    bookId = registry.create({ name: '书', category: '玄幻' }).id
  })

  afterEach(() => rmSync(userDir, { recursive: true, force: true }))

  it('buildFlowContextPayload stores systemPrompt and summary', () => {
    const db = registry.getDb(bookId)
    const entry = new KnowledgeRepository(db).create({
      type: 'fact',
      title: '测试知识',
      content: '内容',
      status: 'approved'
    })
    const payload = buildFlowContextPayload(
      {
        chapterIds: [],
        outlineIds: [],
        settingIds: [],
        inspirationIds: [],
        knowledgeIds: [entry.id]
      },
      registry,
      bookId,
      '作者'
    )
    expect(payload.systemPrompt).toContain('测试知识')
    expect(payload.contextSummary).toContain('1')
    db.close()
  })
})
  • Step 2: 运行确认 FAIL
npm run test -- tests/main/ai-context-builder.test.ts tests/main/flow-context.test.ts
  • Step 3: 扩展 ai-context-builder

要点:

  • KNOWLEDGE_CHAR_LIMIT = 500KNOWLEDGE_TOTAL_LIMIT = 4000
  • knowledgeTypeLabel() 映射五种类型
  • candidates 循环后追加 knowledge 块,带 sortKey: entry.importance * 1000(高 important 先保留)
  • 知识区单独裁剪至 4000 后再参与总 16KB 裁剪
  • 所有现有 build() 调用 binding 需含 knowledgeIds(测试已补)

src/main/services/flow-context.util.ts

import type { AiContextBinding, FlowContextPayload } from '../../shared/types'
import type { BookRegistryService } from './book-registry'
import { aiContextBuilder } from './ai-context-builder.service'

export function buildFlowContextPayload(
  binding: AiContextBinding,
  registry: BookRegistryService,
  bookId: string,
  penName: string
): FlowContextPayload {
  const built = aiContextBuilder.build(binding, registry, bookId, penName)
  const counts = { chapter: 0, outline: 0, setting: 0, inspiration: 0, knowledge: 0 }
  for (const p of built.preview) {
    if (p.kind in counts) counts[p.kind as keyof typeof counts]++
  }
  const contextSummary = `${counts.chapter}章·${counts.outline}纲·${counts.setting}设·${counts.inspiration}感·${counts.knowledge}知`
  return { binding, systemPrompt: built.systemPrompt, contextSummary }
}
  • Step 4: 运行 PASS
npm run test -- tests/main/ai-context-builder.test.ts tests/main/flow-context.test.ts
  • Step 5: Commit
git add src/main/services/ai-context-builder.service.ts src/main/services/flow-context.util.ts tests/main/
git commit -m "feat: extend AiContextBuilder with knowledge blocks and flow payload helper"

Task 4: IPC suggestContext + preload

Files:

  • Modify: src/main/ipc/handlers/knowledge.handler.ts

  • Modify: src/preload/index.ts

  • Modify: src/shared/electron-api.d.ts

  • Modify: src/main/services/global-settings.ts

  • Modify: src/renderer/stores/useSettingsStore.ts

  • Step 1: knowledge.handler 追加

ipcMain.handle(
  IPC.KNOWLEDGE_SUGGEST_CONTEXT,
  (
    _e,
    { bookId, opts }: { bookId: string; opts?: KnowledgeSuggestOptions }
  ) => wrap(() => {
    const settings = /* inject via handler param or read topN from settings */
    new KnowledgeRetrievalService(registry.getDb(bookId)).suggest(opts)
  })
)

registerKnowledgeHandlers 签名改为接收 GlobalSettingsServicetopN 默认 settings.get().knowledgeSuggestTopN ?? 8

  • Step 2: global-settings 默认值
knowledgeAutoSuggest: true,
knowledgeSuggestTopN: 8,

get() merge 补默认。

  • Step 3: preload + electron-api.d.ts
suggestContext: (
  bookId: string,
  opts?: KnowledgeSuggestOptions
): Promise<IpcResult<ScoredKnowledge[]>>
  • Step 4: build
npm run build
  • Step 5: Commit
git commit -m "feat: add knowledge suggestContext IPC and global settings defaults"

Task 5: 写作流 systemPrompt 修复

Files:

  • Modify: src/main/services/interactive-writing.service.ts

  • Modify: src/main/ipc/handlers/interactive.handler.ts

  • Modify: src/main/ipc/handlers/auto.handler.ts

  • Modify: src/main/ipc/handlers/wizard.handler.ts

  • Modify: src/main/services/wizard-writing.service.tsbuildPreview 追加知识列表)

  • Step 1: interactive confirmContext 存 FlowContextPayload

interactive.handler.ts INTERACTIVE_CONFIRM_CONTEXT

const payload = buildFlowContextPayload(binding, registry, bookId, settings.get().penName)
const flow = service.confirmContext(flowId, payload)

InteractiveWritingService.confirmContext 改为:

confirmContext(flowId: string, payload: FlowContextPayload): InteractiveFlow {
  this.repo.updateStep(flowId, 'plot_suggest', {
    contextJson: JSON.stringify(payload)
  })
  return this.repo.get(flowId)!
}

private readContextText(flowId: string): string {
  try {
    const parsed = JSON.parse(this.repo.getContextJson(flowId)) as FlowContextPayload
    return parsed.systemPrompt || '(无上下文)'
  } catch {
    return '(无上下文)'
  }
}

enrichFlow(flow: InteractiveFlow): InteractiveFlow {
  try {
    const parsed = JSON.parse(this.repo.getContextJson(flow.id)) as FlowContextPayload
    return { ...flow, contextSummary: parsed.contextSummary }
  } catch {
    return flow
  }
}
  • Step 2: auto.handler / wizard.handler readContextText

替换本地 readContextText 为读 systemPrompt

function readContextText(registry, bookId, flowId): string {
  const row = registry.getDb(bookId)
    .prepare('SELECT context_json FROM interactive_flows WHERE id = ?')
    .get(flowId) as { context_json: string } | undefined
  try {
    const parsed = JSON.parse(row?.context_json ?? '{}') as FlowContextPayload
    return parsed.systemPrompt ?? '(无上下文)'
  } catch {
    return '(无上下文)'
  }
}
  • Step 3: wizard confirmContext 同 interactive

wizard.handler.ts WIZARD_CONFIRM_CONTEXT 使用 buildFlowContextPayload

  • Step 4: buildPreview 追加知识标题

WizardWritingService.buildPreview 返回前,从 payload.binding.knowledgeIds 查标题,append \n将注入知识:A、B、C

  • Step 5: auto planScenes 前自动 confirm

AutoPanelplanScenes 调用前,若 flow 无 systemPrompt,用 session binding + 默认推荐 knowledgeIds 调 interactive.confirmContext 等价逻辑(可新增 auto:confirmContext IPC 或复用 buildFlowContextPayload + setContextSummary 扩展为存完整 payload)。

推荐:新增 auto.handler AUTO_CONFIRM_CONTEXT 与 interactive 对称。

  • Step 6: Commit
git commit -m "fix: store full systemPrompt in writing flow contextJson for P6"

Task 6: KnowledgeSuggestList + ContextEditorModal

Files:

  • Create: src/renderer/components/ai/KnowledgeSuggestList.tsx

  • Modify: src/renderer/components/ai/ContextEditorModal.tsx

  • Modify: src/renderer/lib/ai-context-summary.ts

  • Step 1: KnowledgeSuggestList

Props

interface KnowledgeSuggestListProps {
  items: ScoredKnowledge[]
  selectedIds: string[]
  onToggle: (id: string) => void
}

渲染 checkbox + title + t(\knowledge.type.${entry.type}`)+ reason badgest(reason)`。

  • Step 2: ContextEditorModal 扩展

新增 propsgoalText?: string

打开时:

const chapterId = useBookStore.getState().selectedChapterId ?? undefined
const suggested = await ipcCall(() =>
  window.electronAPI.knowledge.suggestContext(bookId, { currentChapterId: chapterId, goalText })
)
// 默认 merge suggested ids into binding.knowledgeIds

新增 Tab knowledge;顶部推荐区 + context-refresh-suggest;下方 approved 全列表(knowledge.list(bookId, { status: 'approved' }))可勾选。

  • Step 3: formatContextSummary 含 knowledge
const counts = { chapter: 0, outline: 0, setting: 0, inspiration: 0, knowledge: 0 }
// ...
return t('ai.contextSummary', { ...counts })

更新 ai.contextSummary i18n 为 {{chapters}}章·{{outlines}}纲·{{settings}}设·{{inspirations}}感·{{knowledge}}知

  • Step 4: Commit
git commit -m "feat: add KnowledgeSuggestList and knowledge tab in context editor"

Task 7: 交互 / 自动 / 向导 Panel 集成

Files:

  • Modify: src/renderer/components/ai/InteractivePanel.tsx

  • Modify: src/renderer/components/ai/AutoPanel.tsx

  • Modify: src/renderer/components/ai/WizardPanel.tsx

  • Modify: src/renderer/stores/useInteractiveStore.ts

  • Modify: src/renderer/stores/useAutoStore.tsconfirmContext

  • Modify: src/renderer/components/ai/AiPanel.tsx(传 goalText 空)

  • Step 1: InteractivePanel context_confirm

  • mount 时 suggestContext → state suggested

  • 内联 <KnowledgeSuggestList>data-testid="interactive-knowledge-suggest"

  • 勾选同步到 session context.knowledgeIdsai.sessionUpdate

  • confirmContextAndStart 传含 knowledgeIds 的 binding

  • Step 2: AutoPanel

  • goal_setup 区域下方折叠「知识上下文」data-testid="auto-knowledge-suggest"

  • goalText = chapterGoal + 选中大纲 description

  • planScenes 前调用 auto.confirmContextTask 5 新增 IPC

  • Step 3: WizardPanel wizard_context

  • 同 Interactive 内联推荐列表 data-testid="wizard-knowledge-suggest"

  • handleContextClosegoalText = goal + styleNote

  • Step 4: Commit

git commit -m "feat: integrate knowledge suggest into interactive auto wizard panels"

Task 8: i18n

Files:

  • Modify: public/locales/zh-CN/translation.json

  • Modify: public/locales/en/translation.json

  • Step 1: 追加 spec §9 全部键 + ai.contextSummary 更新

  • Step 2: Commit

git commit -m "feat: add P6 AI context i18n strings"

Task 9: 集成测试

Files:

  • Create: tests/main/ai-context-integration.test.ts

  • Step 1: IT-CTX-01

it('IT-CTX-01: suggestContext returns entries when approved exist', () => {
  // setup approved entry → suggest → length >= 1
})
  • Step 2: IT-CTX-02

通过 aiContextBuilder.build 含 knowledgeIds 断言 systemPrompt【知识·(无需 Electron IPC)。

  • Step 3: IT-CTX-03240s
it('IT-CTX-03: interactive confirm then suggestPlots uses knowledge context', async () => {
  // setup book + knowledge + interactive flow
  // confirmContext with knowledgeIds
  // suggestPlots returns 3 options
}, 240_000)
  • Step 4: 运行
npm run test -- tests/main/ai-context-integration.test.ts
  • Step 5: Commit
git commit -m "test: add P6 AI context integration tests"

Task 10: E2E

Files:

  • Create: e2e/ai-context.spec.ts

  • Step 1: E2E-CTX-01

test('E2E-CTX-01: context editor knowledge tab shows suggestions', async () => {
  // create book → add approved knowledge via panel → open AI context editor → context-tab-knowledge visible
})
  • Step 2: E2E-CTX-02
test('E2E-CTX-02: interactive context_confirm shows knowledge suggest', async () => {
  // open interactive → interactive-knowledge-suggest visible
})
  • Step 3: E2E-CTX-03300s
test('E2E-CTX-03: chat with knowledge context gets AI reply', async () => {
  test.setTimeout(300_000)
  // add knowledge → select in context → send chat → non-empty reply
})
  • Step 4: 运行
npm run test:e2e -- e2e/ai-context.spec.ts
  • Step 5: Commit
git commit -m "test: add P6 AI context E2E specs"

Task 11: v0.7.0 交付

Files:

  • Modify: package.json

  • Modify: README.md

  • Modify: src/renderer/components/settings/SettingsPage.tsxabout 版本号)

  • Modify: docs/superpowers/plans/2026-07-07-bilin-p6-ai-context.md(勾选完成项)

  • Step 1: 版本号 0.7.0

  • Step 2: README 追加

- 知识库 AI 上下文:半自动推荐 + 勾选注入四种写作模式(P6)
  • Step 3: 全量测试
npm run build
npm run test
npm run test:e2e
  • Step 4: Commit
git commit -m "feat: ship v0.7.0 with AI knowledge context integration"

Spec Coverage Checklist

Spec 要求 Task
KnowledgeRetrievalService 打分 Task 2
AiContextBinding.knowledgeIds Task 1
AiContextBuilder 知识块 Task 3
FlowContextPayload / systemPrompt 修复 Task 3, 5
knowledge:suggestContext IPC Task 4
ContextEditorModal 知识 Tab Task 6
对话静默注入 Task 5AI_CHAT 已用 build+ Task 6
交互 context_confirm Task 7
自动写作知识确认 Task 5, 7
向导 wizard_context Task 7
global-settings 默认值 Task 4
UT-CTX-01~04 Task 2, 3
IT-CTX-01~03 Task 9
E2E-CTX-01~03 Task 10
v0.7.0 DoD Task 11