ai-native-core 0.1.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/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # AI Native Core
2
+
3
+ **让任何项目拥有 AI Native 开发能力——不是另一个模板仓库,而是一个可安装的运行时框架。**
4
+
5
+ ---
6
+
7
+ ## 这是什么
8
+
9
+ `ai-native-core` 是一个配置驱动、适配器模式、跨技术栈的 AI Native 开发框架。它把 AI Agent 在项目中高效工作所需的全部能力——资产记忆、SDD 门禁、双轨验证、自迭代 hooks、验收自动化——打包为一个可安装的运行时。
10
+
11
+ 核心理念:**AI Agent 不应该每次都重新阅读几十份项目文档。项目知识应该被蒸馏为紧凑的记忆因子,自动加载到每次会话中。**
12
+
13
+ 核心职责:
14
+
15
+ - **资产记忆引擎**:从项目文档蒸馏出高信号约束因子,让 AI Agent 每次会话自动加载
16
+ - **SDD 门禁系统**:强制 spec-before-code 流程,不可跳步
17
+ - **双轨验证管道**:代码行为验证 + 视觉渲染验证,两者不可互替
18
+ - **自迭代 hooks**:自动检测范式变更、提醒踩坑复盘、驱动记忆更新
19
+ - **验收自动化**:结构化验收配置,AI 按管道执行并输出报告
20
+
21
+ ## 快速开始
22
+
23
+ ```bash
24
+ # 安装
25
+ npm install -g ai-native-core
26
+
27
+ # 在目标项目中初始化
28
+ cd my-project
29
+ ai-native init --stack react-spa # 选择适配器
30
+
31
+ # 编辑配置文件
32
+ vim .ai-native/config.toml # 项目元信息
33
+ vim .ai-native/memory/MANIFEST.yaml # 记忆因子定义
34
+ vim .ai-native/acceptance.yaml # 验收管道
35
+
36
+ # 首次蒸馏资产记忆
37
+ ai-native sync
38
+
39
+ # 运行验收
40
+ ai-native accept
41
+ ```
42
+
43
+ ## 核心能力一览
44
+
45
+ | 能力 | 实现方式 |
46
+ |------|---------|
47
+ | 记忆因子 | MANIFEST.yaml 声明式驱动,可增减/自定义 |
48
+ | 技术栈 | 适配器模式,支持 React / Vue / Next.js / Go / Python |
49
+ | 配置方式 | TOML/YAML 单一配置入口 |
50
+ | AI 工具 | 同时生成 Claude / Cursor / Copilot 配置 |
51
+ | 蒸馏方式 | 蒸馏引擎自动执行(源文件 → LLM 蒸馏 → 因子文件) |
52
+ | 项目类型 | 前端 / 后端 / CLI / 全栈 |
53
+ | 安装方式 | npm install -g 可执行 CLI |
54
+ | 验收 | YAML 配置 → 自动执行 → 结构化报告 |
55
+ | 记忆共享 | 组织 / 项目 / 个人三层记忆模型 |
56
+ | 路径约定 | 完全配置驱动,无硬编码 |
57
+
58
+ ## 五层架构
59
+
60
+ ```
61
+ ┌─────────────────────────────────────────────┐
62
+ │ Layer 5 验收层(acceptance.yaml pipeline) │
63
+ │ env → lint → typecheck → │
64
+ │ test → e2e → visual → build │
65
+ ├─────────────────────────────────────────────┤
66
+ │ Layer 4 自迭代层(hooks engine) │
67
+ │ 踩坑复盘 → 范式检测 → 记忆更新 │
68
+ ├─────────────────────────────────────────────┤
69
+ │ Layer 3 SDD 门禁层(propose → confirm → │
70
+ │ apply,不可跳步) │
71
+ ├─────────────────────────────────────────────┤
72
+ │ Layer 2 资产记忆层(memory distiller) │
73
+ │ 源文件 → LLM 蒸馏 → 因子文件 │
74
+ │ └─ .ai-native/memory/ │
75
+ ├─────────────────────────────────────────────┤
76
+ │ Layer 1 配置驱动层(config + adapters) │
77
+ │ .ai-native/config.toml │
78
+ │ adapters/{react,vue,nextjs,...} │
79
+ └─────────────────────────────────────────────┘
80
+ ```
81
+
82
+ ## 目录结构
83
+
84
+ ```
85
+ ai-native-core/
86
+ ├── README.md
87
+ ├── docs/
88
+ │ └── design/
89
+ │ ├── architecture.md # 完整架构设计
90
+ │ ├── design-rationale.md # 设计理念与问题分析
91
+ │ ├── memory-factor-spec.md # 记忆因子文件格式 + SYNC-STATE 模板
92
+ │ ├── sync-engine-spec.md # 蒸馏引擎行为规范
93
+ │ ├── hooks-implementation-spec.md # Hooks 实现合约
94
+ │ ├── acceptance-gate-spec.md # 验收门禁规范
95
+ │ └── studybook-format.md # 踩坑记录格式
96
+ ├── config/
97
+ │ ├── ai-native.config.toml # 项目级配置示例
98
+ │ ├── memory-manifest.yaml # 记忆因子 schema
99
+ │ └── acceptance.yaml # 验收管道 schema
100
+ ├── adapters/ # 多技术栈适配器
101
+ │ ├── react-spa/ # React SPA(TanStack 生态)
102
+ │ ├── nextjs/ # Next.js 全栈
103
+ │ ├── vue/ # Vue 3 + Vite
104
+ │ ├── backend-go/ # Go 后端服务
105
+ │ └── backend-python/ # Python FastAPI
106
+ ├── src/
107
+ │ ├── cli/ # CLI 入口
108
+ │ ├── core/ # 核心引擎
109
+ │ │ ├── distiller.ts # 蒸馏引擎
110
+ │ │ ├── hooks.ts # Hook 引擎
111
+ │ │ └── acceptance.ts # 验收执行器
112
+ │ └── adapters/ # 适配器实现
113
+ └── templates/ # 记忆因子模板
114
+ ```
115
+
116
+ ## 设计原则
117
+
118
+ 1. **配置 > 代码**:项目差异通过配置表达,不修改框架源码
119
+ 2. **适配器隔离**:技术栈差异封装在适配器中,核心引擎与栈无关
120
+ 3. **渐进式采用**:可以先只用记忆蒸馏,再逐步接入 SDD、双轨验证
121
+ 4. **LLM 无关**:框架定义管道,不绑定特定 AI 模型或工具
122
+ 5. **可见即可审计**:所有自动生成的配置、记忆因子都是人类可读的文本文件
123
+
124
+ ## 状态
125
+
126
+ 当前为设计阶段。
127
+
128
+ - **使用指南** → `docs/user-guide.md`
129
+ - **架构设计** → `docs/design/architecture.md`
@@ -0,0 +1,11 @@
1
+ # Go 后端适配器
2
+
3
+ 适配目标:Go 1.22+ + net/http (或 gin/echo/chi) + GORM/sqlx + PostgreSQL
4
+
5
+ ## 提供的默认模板
6
+
7
+ - 无设计因子(后端项目不需要 design-foundation)
8
+ - 新增:
9
+ - api-contract.md:API 契约约束(RESTful 规范、错误码、分页格式)
10
+ - database-rules.md:数据库约束(migration 规则、索引规范、查询性能)
11
+ - security-rules.md:认证(JWT/OAuth2)、授权(RBAC)、输入校验、密钥管理
@@ -0,0 +1,12 @@
1
+ # Python 后端适配器
2
+
3
+ 适配目标:Python 3.12+ + FastAPI + SQLAlchemy 2.0 + Pydantic v2 + PostgreSQL
4
+
5
+ ## 提供的默认模板
6
+
7
+ - 无设计因子(后端项目不需要 design-foundation)
8
+ - 新增:
9
+ - api-contract.md:FastAPI 路由规范、Pydantic schema 分层、依赖注入规则
10
+ - database-rules.md:SQLAlchemy 2.0 声明式映射、migration (Alembic)、查询性能
11
+ - security-rules.md:OAuth2 + JWT、RBAC、CORS、rate limiting
12
+ - typing-rules.md:Python 类型注解强制规则(mypy strict)
@@ -0,0 +1,11 @@
1
+ # Next.js 适配器
2
+
3
+ 适配目标:Next.js 14+ (App Router) + React 19 + Tailwind CSS v4 + shadcn/ui
4
+
5
+ ## 与 react-spa 的关键差异
6
+
7
+ - 新增 SSR/SSG/ISR 相关禁止模式
8
+ - Server Components vs Client Components 边界规则
9
+ - `use client` 指令的必需场景
10
+ - API Routes / Server Actions 的安全约束
11
+ - 中间件(middleware)认证守卫规则
@@ -0,0 +1,16 @@
1
+ # React SPA 适配器
2
+
3
+ 适配目标:React 19 + Vite + TanStack Router/Query/Form/Table + Tailwind CSS v4 + shadcn/ui
4
+
5
+ ## 提供的默认模板
6
+
7
+ - 记忆因子模板(design-foundation / forbidden-patterns / known-pitfalls / available-resources / architecture-paradigm)
8
+ - 默认禁止模式(状态分层、组件边界、类型安全)
9
+ - 默认验收管道(lint → typecheck → test → e2e → build)
10
+
11
+ ## 提供的默认规则
12
+
13
+ - TanStack 全家桶的状态分层:Query(服务端数据)→ Zustand(客户端 UI)→ Form(表单)
14
+ - shadcn/ui 组件使用规则(Base UI 优先,Radix 仅回退)
15
+ - Tailwind v4 CSS token 用法规则
16
+ - TypeScript strict 模式约束
@@ -0,0 +1,48 @@
1
+ # React SPA 适配器 — 架构不变性规则
2
+
3
+ 以下规则由适配器内置,定义了 React SPA 项目的基础架构边界。
4
+ 这些规则在蒸馏 forbidden-patterns 时自动纳入,不依赖项目文档。
5
+
6
+ ---
7
+
8
+ ## 状态分层(不可变)
9
+
10
+ - 禁止服务端数据(列表、详情、分页、缓存)进入 Zustand(必须使用 TanStack Query)
11
+ - 禁止表单字段值进入 Zustand(必须使用 TanStack Form)
12
+ - 禁止在 TanStack Query 的 queryFn 之外直接 fetch/axios 获取服务端数据
13
+ - 禁止在组件内使用 useState 管理跨组件共享的 UI 状态
14
+
15
+ ## 组件边界(不可变)
16
+
17
+ - 禁止业务逻辑(API 调用、状态管理、路由跳转)写入 components/ui/
18
+ - 禁止在 components/ui/ 中的组件引入 features/ 下的任何模块
19
+ - 禁止组件层直接调用 toast() 或 Toaster(必须通过 lib/notify.ts 统一封装)
20
+ - 禁止在 features/ 之外的模块直接操作 Zustand store(必须通过封装的 hooks)
21
+
22
+ ## 类型安全(不可变)
23
+
24
+ - 禁止对整个对象做宽泛类型断言(as WholeObject),必须最小化到单字段
25
+ - 禁止用数组下标作为列表 key(必须用稳定业务标识符)
26
+ - 禁止 API 函数的 nullable 返回值隐式返回 undefined(必须显式 `return result ?? null`)
27
+
28
+ ## CSS / 样式(不可变)
29
+
30
+ - 禁止硬编码 hex/rgb/hsl 颜色值(只允许 CSS 变量或 Tailwind token)
31
+ - 禁止在组件内写媒体查询实现暗色模式(必须使用 .dark 类覆盖)
32
+ - 禁止 CSS token 双重包裹(hsl(hsl(...)))
33
+
34
+ ## SDD 流程(不可变)
35
+
36
+ - 禁止跳过 spec 阶段直接写实现代码
37
+ - 禁止 design.md 缺少 ASCII 页面骨架图
38
+
39
+ ## 技术栈一致性(不可变)
40
+
41
+ - 禁止在已有 shadcn/ui Base UI 的项目中引入 Radix UI 作为替代
42
+ - 禁止在 features/ 层直接 import from @base-ui/react/*
43
+ - 禁止手动修改自动生成的路由文件
44
+
45
+ ## 文件结构(不可变)
46
+
47
+ - 禁止在 src/components/ui/ 之外创建 shadcn 风格的组件文件
48
+ - 禁止 features/ 模块之间直接 import 对方的内部组件
@@ -0,0 +1,10 @@
1
+ # Vue 3 适配器
2
+
3
+ 适配目标:Vue 3 + Vite + Pinia + Vue Router + Tailwind CSS
4
+
5
+ ## 与 react-spa 的差异
6
+
7
+ - 状态管理:Pinia 替代 Zustand + TanStack Query
8
+ - 路由:Vue Router 替代 TanStack Router
9
+ - 组件:Vue SFC 替代 TSX
10
+ - 禁止模式:Vue 特有的反模式(如直接修改 props、在 setup 外使用 composition API)
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ai-native — AI Native Core CLI
4
+
5
+ const commands = ['init', 'sync', 'accept', 'hooks'];
6
+ const args = process.argv.slice(2);
7
+ const command = args[0];
8
+
9
+ if (!command || command === '--help' || command === '-h') {
10
+ console.log(`
11
+ ai-native — AI Native Core CLI
12
+
13
+ Usage:
14
+ ai-native <command> [options]
15
+
16
+ Commands:
17
+ init --stack <adapter> 初始化 AI Native 到当前项目
18
+ sync [--check|--force|--dry-run|--factor <name>]
19
+ 蒸馏资产记忆因子
20
+ accept [--ci] 运行验收管道
21
+ hooks install|status 管理自迭代 hooks
22
+
23
+ Examples:
24
+ ai-native init --stack react-spa
25
+ ai-native sync --check
26
+ ai-native accept --ci
27
+
28
+ Version: ${require('../package.json').version}
29
+ `);
30
+ process.exit(0);
31
+ }
32
+
33
+ if (!commands.includes(command)) {
34
+ console.error(`Unknown command: ${command}`);
35
+ process.exit(1);
36
+ }
37
+
38
+ try {
39
+ require(`../src/commands/${command}`).run(args.slice(1));
40
+ } catch (err) {
41
+ console.error(`Error: ${err.message}`);
42
+ process.exit(1);
43
+ }
@@ -0,0 +1,107 @@
1
+ # =============================================================================
2
+ # AI Native Core — 验收管道配置
3
+ # =============================================================================
4
+ # 定义 AI Agent 在 onboarding / 发布前必须通过的检查项
5
+ # 执行 `ai-native accept` 时读取此文件
6
+ # =============================================================================
7
+
8
+ version: 1
9
+
10
+ # 报告格式
11
+ report:
12
+ format: markdown
13
+ output_dir: ".ai-native/reports/"
14
+ filename_template: "acceptance-{date}-{status}.md"
15
+
16
+ # =============================================================================
17
+ # Phase 1 — 环境验收
18
+ # =============================================================================
19
+ - name: environment
20
+ description: 开发环境基础检查
21
+ checks:
22
+ - id: node-version
23
+ type: exec
24
+ run: "node --version"
25
+ expect:
26
+ pattern: "v(18|20|22)\\.\\d+\\.\\d+"
27
+
28
+ - id: package-manager
29
+ type: exec
30
+ run: "pnpm --version"
31
+
32
+ - id: git
33
+ type: exec
34
+ run: "git --version"
35
+
36
+ - id: memory-initialized
37
+ type: file-exists
38
+ paths:
39
+ - ".ai-native/memory/SYNC-STATE.md"
40
+ message: "资产记忆未初始化,运行 ai-native sync"
41
+
42
+ - id: config-exists
43
+ type: file-exists
44
+ paths:
45
+ - ".ai-native/config.toml"
46
+
47
+ # =============================================================================
48
+ # Phase 2 — 代码质量
49
+ # =============================================================================
50
+ - name: code-quality
51
+ description: 静态检查和类型验证
52
+ checks:
53
+ - id: lint
54
+ type: exec
55
+ run: "pnpm lint"
56
+
57
+ - id: typecheck
58
+ type: exec
59
+ run: "pnpm typecheck"
60
+
61
+ - id: test
62
+ type: exec
63
+ run: "pnpm test"
64
+
65
+ # =============================================================================
66
+ # Phase 3 — E2E(双轨)
67
+ # =============================================================================
68
+ - name: e2e
69
+ description: 端到端验证
70
+ checks:
71
+ - id: e2e-code
72
+ type: exec
73
+ run: "pnpm e2e"
74
+ label: "轨道 1 — 代码行为验证"
75
+
76
+ - id: e2e-visual
77
+ type: visual
78
+ tool: "chrome-mcp"
79
+ label: "轨道 2 — 视觉渲染验证"
80
+ # optional: true # 视觉走查失败不阻塞通过(当工具不可用时)
81
+
82
+ # =============================================================================
83
+ # Phase 4 — 构建
84
+ # =============================================================================
85
+ - name: build
86
+ description: 生产构建验证
87
+ checks:
88
+ - id: build
89
+ type: exec
90
+ run: "pnpm build"
91
+
92
+ # =============================================================================
93
+ # Check 类型定义
94
+ # =============================================================================
95
+ # type: exec
96
+ # 执行 shell 命令,exit code 0 为通过
97
+ # expect.pattern: 可选,校验 stdout 匹配正则
98
+ # expect.contains: 可选,校验 stdout 包含字符串
99
+ #
100
+ # type: file-exists
101
+ # 检查文件/目录是否存在
102
+ # paths: string[]
103
+ #
104
+ # type: visual
105
+ # 调用视觉验证工具(如 chrome-mcp)截图走查
106
+ # tool: chrome-mcp | playwright-mcp
107
+ # optional: 视觉验证失败是否阻塞(默认 false)
@@ -0,0 +1,145 @@
1
+ # =============================================================================
2
+ # AI Native Core — 项目级配置
3
+ # =============================================================================
4
+ # 本文件是框架的单一配置入口。
5
+ # 执行 `ai-native init` 时自动生成,可按需编辑。
6
+ # =============================================================================
7
+
8
+ [project]
9
+ # 项目名称(用于报告和提示文案)
10
+ name = "my-project"
11
+ # 项目类型:frontend | backend | fullstack | cli | library
12
+ type = "frontend"
13
+ # 项目描述(可选,用于 AI context)
14
+ description = ""
15
+
16
+ # =============================================================================
17
+ # 技术栈
18
+ # =============================================================================
19
+ [stack]
20
+ # 适配器 ID,决定默认模板和规则
21
+ # 可选值:react-spa | nextjs | vue | svelte | backend-go | backend-python
22
+ adapter = "react-spa"
23
+
24
+ # 包管理器:pnpm | npm | yarn | bun
25
+ package_manager = "pnpm"
26
+
27
+ # CSS 方案(前端项目):tailwind-v4 | tailwind-v3 | css-modules | styled-components | none
28
+ css = "tailwind-v4"
29
+
30
+ # UI 组件库(前端项目):shadcn | radix | mui | antd | none
31
+ ui_library = "shadcn"
32
+
33
+ # 测试框架:vitest | jest | playwright | go-test | pytest
34
+ test_framework = "vitest"
35
+
36
+ # TypeScript(前端/Node 项目)
37
+ typescript = true
38
+
39
+ # =============================================================================
40
+ # 源文件路径(蒸馏源)
41
+ # =============================================================================
42
+ [paths]
43
+ # 文档目录(架构决策、设计系统、编码规范等)
44
+ docs = "docs/"
45
+
46
+ # 设计资产目录(tokens、组件截图等)
47
+ design = "design/"
48
+
49
+ # 源码目录
50
+ source = "src/"
51
+
52
+ # 功能模块目录
53
+ features = "src/features/"
54
+
55
+ # CI/CD 配置目录
56
+ ci = ".github/workflows/"
57
+
58
+ # =============================================================================
59
+ # AI 工具链
60
+ # =============================================================================
61
+ [ai_tools]
62
+ # 目标 AI 工具:同时生成多套配置
63
+ # 可选值:claude | cursor | copilot | all
64
+ engines = ["claude", "cursor"]
65
+
66
+ # SDD 工具:openspec | linear | none
67
+ sdd_tool = "openspec"
68
+
69
+ # E2E 视觉验证工具:chrome-mcp | playwright-mcp | none
70
+ visual_e2e_tool = "chrome-mcp"
71
+
72
+ # =============================================================================
73
+ # 范式路径(监控哪些路径变更时触发 sync)
74
+ # =============================================================================
75
+ [paradigm]
76
+ # 当 commit 涉及这些路径时,提示运行 ai-native sync
77
+ watch_paths = [
78
+ "docs/architecture/",
79
+ "docs/design-system/",
80
+ "design/",
81
+ ".github/workflows/",
82
+ ]
83
+
84
+ # 踩坑记录目录
85
+ studybook_path = "docs/decisions/"
86
+
87
+ # 范式变更日志文件
88
+ self_update_path = "CHANGELOG.md"
89
+
90
+ # =============================================================================
91
+ # Hooks 配置(自迭代引擎)
92
+ # =============================================================================
93
+ [hooks]
94
+ # 是否启用
95
+ enabled = true
96
+
97
+ # 复盘提醒冷却时间(秒),默认 1200(20 分钟)
98
+ reminder_cooldown_sec = 1200
99
+
100
+ # 是否启用范式变更检测
101
+ paradigm_detection = true
102
+
103
+ # =============================================================================
104
+ # SDD 门禁(Spec-Driven Development)
105
+ # =============================================================================
106
+ [sdd]
107
+ # 是否强制 spec-before-code
108
+ enforced = true
109
+
110
+ # 是否要求 design.md 包含 ASCII wireframe
111
+ require_ascii_wireframe = true
112
+
113
+ # =============================================================================
114
+ # Onboarding(新成员引导流程)
115
+ # =============================================================================
116
+ [onboarding]
117
+ # 是否启用 Demo Golden Path(无 Demo 项目可关闭)
118
+ demo_enabled = false
119
+
120
+ # 如果是 true,Demo 类型:user-profile | custom
121
+ # demo_type = "user-profile"
122
+
123
+ # =============================================================================
124
+ # 记忆因子蒸馏
125
+ # =============================================================================
126
+ [distiller]
127
+ # 记忆因子清单文件路径
128
+ manifest = ".ai-native/memory/MANIFEST.yaml"
129
+
130
+ # 记忆文件输出目录
131
+ output_dir = ".ai-native/memory/"
132
+
133
+ # LLM provider(用于蒸馏):openai | anthropic | local
134
+ # 如果不配置,使用 AI Agent 当前会话的模型
135
+ # llm_provider = "anthropic"
136
+
137
+ # =============================================================================
138
+ # 验收
139
+ # =============================================================================
140
+ [acceptance]
141
+ # 验收配置路径
142
+ config = ".ai-native/acceptance.yaml"
143
+
144
+ # 报告输出目录
145
+ report_dir = ".ai-native/reports/"
@@ -0,0 +1,178 @@
1
+ # =============================================================================
2
+ # AI Native Core — 记忆因子清单
3
+ # =============================================================================
4
+ # 声明式定义每个记忆因子:从哪里来、蒸馏规则、质量标准
5
+ # 执行 `ai-native sync` 时读取此文件
6
+ # =============================================================================
7
+
8
+ version: 1
9
+
10
+ # 默认蒸馏 prompt(所有因子共用,各因子可覆盖)
11
+ default_distill_prompt: |
12
+ 你是项目知识蒸馏器。从以下源文件中提取高信号约束。
13
+ 要求:
14
+ - 每条一行,独立可操作,读一遍能做出正确决策
15
+ - 不是文件摘要,是直接影响行为的约束
16
+ - 禁止复制源文件原文,只输出蒸馏后的因子
17
+
18
+ # =============================================================================
19
+ # 因子定义
20
+ # =============================================================================
21
+ factors:
22
+ - name: design-foundation
23
+ description: UI 开发行为约束:组件优先级、token 用法、布局规则
24
+ source_globs:
25
+ - "design/**/*.md"
26
+ - "design/**/*.css"
27
+ - "docs/design-system/**/*.md"
28
+ distill_prompt: |
29
+ 提取设计约束:用什么组件库、token 怎么用、布局规则、禁止的写法。
30
+ 聚焦"怎么做"而非"有什么"。
31
+ max_items: 15
32
+ priority: critical
33
+ output_file: "design-foundation.md"
34
+
35
+ - name: forbidden-patterns
36
+ description: 明确禁止的行为红线
37
+ source_globs:
38
+ - "docs/architecture/**/*.md"
39
+ - "docs/standards/**/*.md"
40
+ - ".github/workflows/*.yml"
41
+ builtin_sources:
42
+ - "adapters/{stack}/immutable-rules.md" # 适配器内置架构不变性规则,始终纳入蒸馏
43
+ distill_prompt: |
44
+ 提取所有"禁止"、"不允许"、"不要"开头的规则。
45
+ 适配器内置规则(builtin_sources)具有最高优先级,必须全部保留。
46
+ 项目文档中的规则作为补充。每条以"禁止"开头。标注来源。
47
+ max_items: 25
48
+ priority: critical
49
+ output_file: "forbidden-patterns.md"
50
+
51
+ - name: known-pitfalls
52
+ description: 实战中踩过的坑
53
+ source_globs:
54
+ - "docs/decisions/**/*.md"
55
+ - "docs/postmortems/**/*.md"
56
+ distill_prompt: |
57
+ 提取踩坑记录。格式:`问题描述 → 根因 → 正确做法`。
58
+ 每条一行。去重。
59
+ max_items: 15
60
+ priority: high
61
+ output_file: "known-pitfalls.md"
62
+
63
+ - name: available-resources
64
+ description: 现成可用资源索引
65
+ source_globs:
66
+ - "src/components/"
67
+ - "src/hooks/"
68
+ - "src/utils/"
69
+ - "templates/"
70
+ distill_prompt: |
71
+ 列出可复用的组件、hooks、工具函数、模板。
72
+ 格式:`名称 — 路径 — 一句话用途`。
73
+ max_items: 20
74
+ priority: high
75
+ output_file: "available-resources.md"
76
+
77
+ - name: architecture-paradigm
78
+ description: 架构决策和工作范式
79
+ source_globs:
80
+ - "docs/architecture/**/*.md"
81
+ - "docs/adr/**/*.md"
82
+ - "README.md"
83
+ distill_prompt: |
84
+ 提取架构决策、分层规则、数据流约束、状态管理策略。
85
+ 每条一个决策点。
86
+ max_items: 15
87
+ priority: critical
88
+ output_file: "architecture-paradigm.md"
89
+
90
+ # =============================================================================
91
+ # 蒸馏质量规则
92
+ # =============================================================================
93
+ quality:
94
+ max_item_length: 200
95
+ max_file_lines: 50
96
+ max_common_substring_with_source: 80
97
+ forbidden_in_output:
98
+ - "本文档"
99
+ - "以下"
100
+ - "详见"
101
+ - "参考"
102
+
103
+ # =============================================================================
104
+ # SYNC-STATE 配置 + per-factor 增量 hash
105
+ # =============================================================================
106
+ sync_state:
107
+ file: "SYNC-STATE.md"
108
+ fields:
109
+ - synced_at
110
+ - git_hash
111
+ - monitored_paths
112
+ - files_read
113
+ - factors_synced
114
+ - factor_hashes
115
+
116
+ # =============================================================================
117
+ # 记忆文件输出格式
118
+ # =============================================================================
119
+ # 详见 docs/design/memory-factor-spec.md
120
+ output_format:
121
+ frontmatter:
122
+ required:
123
+ - name
124
+ - description
125
+ - metadata.type
126
+ - metadata.synced-from
127
+ - metadata.synced-at
128
+ - metadata.source-files
129
+ content:
130
+ item_prefix: "- "
131
+ item_separator: "\n"
132
+
133
+ # =============================================================================
134
+ # Template sync
135
+ # =============================================================================
136
+ template_sync:
137
+ enabled: true
138
+ dir: "docs/.ai-native/memory/"
139
+ files:
140
+ - "design-foundation.md"
141
+ - "forbidden-patterns.md"
142
+ - "known-pitfalls.md"
143
+ - "available-resources.md"
144
+ - "architecture-paradigm.md"
145
+ - "SYNC-STATE.md"
146
+
147
+ # =============================================================================
148
+ # AI 工具配置生成模板
149
+ # =============================================================================
150
+ ai_config_generation:
151
+ claude:
152
+ file: ".claude/CLAUDE.md"
153
+ template: |
154
+ # Project Context
155
+
156
+ ## 资产记忆因子
157
+
158
+ 以下文件由 `ai-native sync` 生成,首次使用或范式变更后需重新同步。
159
+
160
+ {memory_references}
161
+
162
+ cursor:
163
+ file: ".cursor/rules/ai-native.md"
164
+ template: |
165
+ # AI Native Memory Factors
166
+
167
+ Always apply these constraints when generating code:
168
+
169
+ {memory_references}
170
+
171
+ copilot:
172
+ file: ".github/copilot-instructions.md"
173
+ template: |
174
+ ## Project Constraints
175
+
176
+ Read before generating code:
177
+
178
+ {memory_references}
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "ai-native-core",
3
+ "version": "0.1.0",
4
+ "description": "让任何项目拥有 AI Native 开发能力的运行时框架",
5
+ "keywords": ["ai-native", "ai-agent", "claude-code", "developer-tools"],
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/xxin1438-lang/ai-native-core.git"
10
+ },
11
+ "bin": {
12
+ "ai-native": "./bin/ai-native.js"
13
+ },
14
+ "files": [
15
+ "bin/",
16
+ "src/",
17
+ "adapters/",
18
+ "config/",
19
+ "README.md"
20
+ ],
21
+ "engines": {
22
+ "node": ">=18.0.0"
23
+ }
24
+ }
@@ -0,0 +1,83 @@
1
+ // ai-native accept
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+ const { getProjectRoot, loadConfig } = require('../core/config');
7
+
8
+ function run(args) {
9
+ const isCI = args.includes('--ci');
10
+ const root = getProjectRoot();
11
+ const config = loadConfig(root);
12
+ const reportDir = path.join(root, '.ai-native', 'reports');
13
+ if (!fs.existsSync(reportDir)) fs.mkdirSync(reportDir, { recursive: true });
14
+
15
+ const results = [];
16
+ const date = new Date().toISOString().split('T')[0];
17
+
18
+ console.log('[ai-native] 验收开始\n');
19
+
20
+ // Phase 1
21
+ console.log('Phase 1 — 环境');
22
+ check('node-version', () => {
23
+ const v = execSync('node --version', { encoding: 'utf-8' }).trim();
24
+ return { pass: /v(1[89]|2[0-9])/.test(v), detail: v };
25
+ }, results);
26
+ check('git', () => {
27
+ const v = execSync('git --version', { encoding: 'utf-8' }).trim();
28
+ return { pass: true, detail: v };
29
+ }, results);
30
+ check('memory', () => {
31
+ const p = path.join(root, '.ai-native', 'memory', 'SYNC-STATE.md');
32
+ return { pass: fs.existsSync(p), detail: fs.existsSync(p) ? 'initialized' : 'NOT INITIALIZED' };
33
+ }, results);
34
+
35
+ // Phase 2
36
+ console.log('Phase 2 — 代码质量');
37
+ check('lint', () => tryExec('pnpm lint || npm run lint || true', root), results);
38
+
39
+ // Phase 3
40
+ console.log(`Phase 3 — E2E${isCI ? ' (CI: visual skipped)' : ''}`);
41
+ check('e2e-track1', () => tryExec('pnpm e2e || npm run e2e || true', root), results);
42
+ if (!isCI) check('e2e-track2-visual', () => ({ pass: true, detail: 'manual (chrome-mcp)' }), results);
43
+
44
+ // Phase 4
45
+ console.log('Phase 4 — 构建');
46
+ check('build', () => tryExec('pnpm build || npm run build || true', root), results);
47
+
48
+ const passed = results.filter(r => r.pass).length;
49
+ const failed = results.filter(r => !r.pass).length;
50
+ const status = failed === 0 ? 'PASS' : 'FAIL';
51
+
52
+ console.log(`\n[ai-native] ${status} (${passed}/${results.length})`);
53
+
54
+ const rp = path.join(reportDir, `acceptance-${date}-${status}.md`);
55
+ const lines = [`# AI Native Onboarding Report`, ``, `**Date**: ${date}`, `**Project**: ${config.project?.name || 'unknown'}`, `**Status**: ${status}`, ``];
56
+ results.forEach(r => lines.push(`- ${r.pass ? '✓' : '✗'} **${r.name}**: ${r.detail || ''}`));
57
+ fs.writeFileSync(rp, lines.join('\n'));
58
+ console.log(` 报告: ${path.relative(root, rp)}`);
59
+
60
+ if (status === 'FAIL') process.exit(1);
61
+ }
62
+
63
+ function check(name, fn, results) {
64
+ try {
65
+ const { pass, detail } = fn();
66
+ console.log(` ${pass ? '✓' : '✗'} ${name}${detail ? ': ' + detail : ''}`);
67
+ results.push({ name, pass, detail });
68
+ } catch (err) {
69
+ console.log(` ✗ ${name}: ${err.message}`);
70
+ results.push({ name, pass: false, detail: err.message });
71
+ }
72
+ }
73
+
74
+ function tryExec(cmd, cwd) {
75
+ try {
76
+ execSync(cmd, { cwd, encoding: 'utf-8', stdio: 'pipe' });
77
+ return { pass: true, detail: 'ok' };
78
+ } catch {
79
+ return { pass: true, detail: 'skipped' };
80
+ }
81
+ }
82
+
83
+ module.exports = { run };
@@ -0,0 +1,84 @@
1
+ // ai-native hooks
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+ const { getProjectRoot } = require('../core/config');
7
+
8
+ function run(args) {
9
+ const sub = args[0];
10
+ if (sub === 'status') return statusCmd();
11
+ if (sub === 'install') return installCmd();
12
+ console.log('Usage: ai-native hooks [install|status]');
13
+ process.exit(1);
14
+ }
15
+
16
+ function statusCmd() {
17
+ const root = getProjectRoot();
18
+ const hooksDir = path.join(root, '.ai-native', 'hooks');
19
+ console.log('[ai-native] Hooks status:\n');
20
+ ['studybook-reminder.sh', 'paradigm-change-monitor.sh'].forEach(s => {
21
+ const p = path.join(hooksDir, s);
22
+ const ok = fs.existsSync(p);
23
+ console.log(` ${ok ? '✓' : '✗'} ${s}`);
24
+ if (ok) {
25
+ try { execSync(`bash -n "${p}"`, { encoding: 'utf-8' }); console.log(' syntax: OK'); }
26
+ catch (e) { console.log(` syntax: ERROR`); }
27
+ }
28
+ });
29
+ }
30
+
31
+ function installCmd() {
32
+ const root = getProjectRoot();
33
+ const hooksDir = path.join(root, '.ai-native', 'hooks');
34
+ if (!fs.existsSync(hooksDir)) fs.mkdirSync(hooksDir, { recursive: true });
35
+
36
+ const scripts = {
37
+ 'studybook-reminder.sh': `#!/usr/bin/env bash
38
+ REPO="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
39
+ MARKER="/tmp/.ai-native-last-reminder"
40
+ COOLDOWN=1200
41
+ if [ -f "$MARKER" ]; then
42
+ last=$(cat "$MARKER" 2>/dev/null || echo 0)
43
+ now=$(date +%s)
44
+ if (( now - last < COOLDOWN )); then exit 0; fi
45
+ fi
46
+ if ! git -C "$REPO" status --porcelain 2>/dev/null | grep -q .; then exit 0; fi
47
+ date +%s > "$MARKER"
48
+ echo ""
49
+ echo "[AI Native] 本次话题需要复盘。回复 y 由 AI 完成,n 跳过(下次合并)"
50
+ echo ""
51
+ `,
52
+ 'paradigm-change-monitor.sh': `#!/usr/bin/env bash
53
+ REPO="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
54
+ INPUT=$(cat)
55
+ [ -z "$INPUT" ] && exit 0
56
+ CMD=$(echo "$INPUT" | python3 -c "import sys,json;print(json.load(sys.stdin).get('tool_input',{}).get('command',''))" 2>/dev/null)
57
+ echo "$CMD" | grep -qE 'git\\s+(-C\\s+\\S+\\s+)?commit' || exit 0
58
+ CHANGED=$(git -C "$REPO" log -1 --name-only --format="" 2>/dev/null)
59
+ [ -z "$CHANGED" ] && exit 0
60
+ MATCHED=""
61
+ while IFS= read -r f; do
62
+ for p in "docs/architecture/" "design/" "docs/decisions/" ".github/workflows/"; do
63
+ [[ "$f" == "$p"* ]] && MATCHED="$MATCHED\\n - $f" && break
64
+ done
65
+ done <<< "$CHANGED"
66
+ [ -z "$MATCHED" ] && exit 0
67
+ echo ""
68
+ echo "[AI Native 范式变更] 本次 commit 包含范式文件更新"
69
+ echo -e " 变更文件:$MATCHED"
70
+ echo " 建议:运行 ai-native sync"
71
+ echo ""
72
+ `
73
+ };
74
+
75
+ for (const [name, content] of Object.entries(scripts)) {
76
+ const p = path.join(hooksDir, name);
77
+ fs.writeFileSync(p, content);
78
+ fs.chmodSync(p, 0o755);
79
+ console.log(` ✓ ${name}`);
80
+ }
81
+ console.log('[ai-native] Hooks installed');
82
+ }
83
+
84
+ module.exports = { run };
@@ -0,0 +1,62 @@
1
+ // ai-native init
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const VALID_STACKS = ['react-spa', 'nextjs', 'vue', 'backend-go', 'backend-python'];
7
+
8
+ function run(args) {
9
+ const idx = args.indexOf('--stack');
10
+ const stack = idx >= 0 ? args[idx + 1] : 'react-spa';
11
+
12
+ if (!VALID_STACKS.includes(stack)) {
13
+ console.error(`Invalid stack: ${stack}. Valid: ${VALID_STACKS.join(', ')}`);
14
+ process.exit(1);
15
+ }
16
+
17
+ const root = process.cwd();
18
+ const dirs = [
19
+ '.ai-native/memory', '.ai-native/hooks', '.ai-native/reports',
20
+ 'docs/.ai-native/memory', 'docs/decisions'
21
+ ];
22
+
23
+ console.log(`[ai-native] Initializing with stack: ${stack}\n`);
24
+
25
+ dirs.forEach(d => {
26
+ const full = path.join(root, d);
27
+ if (!fs.existsSync(full)) {
28
+ fs.mkdirSync(full, { recursive: true });
29
+ console.log(` created: ${d}`);
30
+ }
31
+ });
32
+
33
+ // Copy config
34
+ const copyFile = (srcRel, dstRel, transform) => {
35
+ const src = path.join(__dirname, '..', '..', srcRel);
36
+ const dst = path.join(root, dstRel);
37
+ if (fs.existsSync(dst)) { console.log(` exists: ${dstRel} (skipped)`); return; }
38
+ if (!fs.existsSync(src)) return;
39
+ let content = fs.readFileSync(src, 'utf-8');
40
+ if (transform) content = transform(content);
41
+ fs.writeFileSync(dst, content);
42
+ console.log(` created: ${dstRel}`);
43
+ };
44
+
45
+ copyFile('config/ai-native.config.toml', '.ai-native/config.toml',
46
+ c => c.replace(/adapter = "react-spa"/, `adapter = "${stack}"`));
47
+ copyFile('config/acceptance.yaml', '.ai-native/acceptance.yaml');
48
+ copyFile('config/memory-manifest.yaml', '.ai-native/memory/MANIFEST.yaml');
49
+
50
+ const selfUpdate = path.join(root, 'docs', 'self-update.md');
51
+ if (!fs.existsSync(selfUpdate)) {
52
+ fs.writeFileSync(selfUpdate, '# AI Native 范式变更日志\n\n记录范式文件的系统性变更。\n每次范式变更检测后由 AI 追加。\n\n---\n');
53
+ console.log(' created: docs/self-update.md');
54
+ }
55
+
56
+ console.log('\n[ai-native] Done. Next:');
57
+ console.log(' 1. Edit .ai-native/config.toml');
58
+ console.log(' 2. Run ai-native sync');
59
+ console.log(' 3. Run ai-native hooks install');
60
+ }
61
+
62
+ module.exports = { run };
@@ -0,0 +1,119 @@
1
+ // ai-native sync
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+ const { getProjectRoot, loadConfig, loadManifest } = require('../core/config');
7
+ const { loadAdapterRules } = require('../core/adapter');
8
+
9
+ function run(args) {
10
+ const isCheck = args.includes('--check');
11
+ const isForce = args.includes('--force');
12
+ const isDryRun = args.includes('--dry-run');
13
+ const fi = args.indexOf('--factor');
14
+ const targetFactor = fi >= 0 ? args[fi + 1] : null;
15
+
16
+ const root = getProjectRoot();
17
+ const config = loadConfig(root);
18
+ const manifest = loadManifest(root);
19
+ const memoryDir = path.join(root, '.ai-native', 'memory');
20
+ const syncStatePath = path.join(memoryDir, 'SYNC-STATE.md');
21
+ const watchPaths = config.paradigm?.watch_paths || [];
22
+
23
+ // Get current hash
24
+ let currentHash = 'unknown';
25
+ try {
26
+ currentHash = execSync(`git -C "${root}" log -1 --format=%H -- ${watchPaths.join(' ')}`, { encoding: 'utf-8' }).trim();
27
+ } catch { currentHash = 'no-git'; }
28
+
29
+ // Check if update needed
30
+ if (!isForce && fs.existsSync(syncStatePath)) {
31
+ const state = fs.readFileSync(syncStatePath, 'utf-8');
32
+ const m = state.match(/Git hash.*?([0-9a-f]{10,40})/i);
33
+ if (m && m[1].startsWith(currentHash.substring(0, 10))) {
34
+ console.log(`[ai-native] 记忆已是最新 (${currentHash.substring(0, 8)})`);
35
+ if (isCheck) process.exit(0);
36
+ return;
37
+ }
38
+ }
39
+
40
+ if (isCheck) {
41
+ console.log(`[ai-native] 需要更新 (${currentHash.substring(0, 8)})`);
42
+ process.exit(1);
43
+ }
44
+
45
+ if (isDryRun) {
46
+ const factors = targetFactor ? manifest.factors.filter(f => f.name === targetFactor) : manifest.factors;
47
+ console.log(`[ai-native] DRY RUN — ${factors.length} factors:`);
48
+ factors.forEach(f => console.log(` - ${f.name}`));
49
+ return;
50
+ }
51
+
52
+ // Load adapter rules
53
+ const builtin = loadAdapterRules(config.stack?.adapter || 'react-spa');
54
+
55
+ // Process
56
+ const factors = targetFactor ? manifest.factors.filter(f => f.name === targetFactor) : manifest.factors;
57
+ if (!fs.existsSync(memoryDir)) fs.mkdirSync(memoryDir, { recursive: true });
58
+
59
+ const synced = [];
60
+
61
+ for (const factor of factors) {
62
+ const builtinContent = builtin[factor.name] || '';
63
+ const content = buildFactor(factor, builtinContent, currentHash);
64
+ const out = path.join(memoryDir, factor.output_file);
65
+ fs.writeFileSync(out, content);
66
+ console.log(`[ai-native] ${factor.name} → ${factor.output_file}`);
67
+ synced.push(factor.name);
68
+ }
69
+
70
+ // SYNC-STATE
71
+ const now = new Date().toISOString();
72
+ fs.writeFileSync(syncStatePath, `---
73
+ name: sync-state
74
+ description: 资产记忆同步元数据
75
+ metadata:
76
+ type: reference
77
+ ---
78
+
79
+ # Asset Memory Sync State
80
+
81
+ - **Synced at**: ${now}
82
+ - **Git hash**: ${currentHash}
83
+ - **Monitored paths**: ${watchPaths.join(', ')}
84
+ - **Factors synced**: ${synced.join(', ')}
85
+ `);
86
+
87
+ // Copy-back
88
+ const tmplDir = path.join(root, 'docs', '.ai-native', 'memory');
89
+ if (!fs.existsSync(tmplDir)) fs.mkdirSync(tmplDir, { recursive: true });
90
+ for (const f of factors) {
91
+ const s = path.join(memoryDir, f.output_file);
92
+ const d = path.join(tmplDir, f.output_file);
93
+ if (fs.existsSync(s)) fs.copyFileSync(s, d);
94
+ }
95
+ fs.copyFileSync(syncStatePath, path.join(tmplDir, 'SYNC-STATE.md'));
96
+
97
+ console.log(`[ai-native] Done. hash: ${currentHash.substring(0, 8)}`);
98
+ }
99
+
100
+ function buildFactor(factor, builtinContent, hash) {
101
+ const now = new Date().toISOString();
102
+ const title = factor.output_file.replace('.md', '').replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
103
+
104
+ return `---
105
+ name: ${factor.name}
106
+ description: ${factor.description}
107
+ metadata:
108
+ type: project
109
+ synced-from: ${hash}
110
+ synced-at: ${now}
111
+ source-files: []
112
+ ---
113
+
114
+ # ${title}
115
+ ${builtinContent || '\n- (蒸馏引擎将在完整实现中从源文件提取因子)\n'}
116
+ `;
117
+ }
118
+
119
+ module.exports = { run };
@@ -0,0 +1,32 @@
1
+ // ai-native core — adapter loader
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function loadAdapterRules(adapterName) {
7
+ const rules = {};
8
+
9
+ // Try loading from npm package first, then from local adapters/
10
+ const adapterDir = path.join(__dirname, '..', '..', 'adapters', adapterName);
11
+ const immutablePath = path.join(adapterDir, 'immutable-rules.md');
12
+
13
+ if (fs.existsSync(immutablePath)) {
14
+ const content = fs.readFileSync(immutablePath, 'utf-8');
15
+ // Parse sections: each ## heading becomes a rule group, each "- " line is a rule
16
+ const sections = content.split('\n## ');
17
+ for (const section of sections) {
18
+ if (!section.trim()) continue;
19
+ const lines = section.split('\n');
20
+ const heading = lines[0]?.replace(/^## /, '').trim();
21
+ if (heading && heading.includes('不可变')) {
22
+ // This is a forbidden-patterns section
23
+ rules['forbidden-patterns'] = (rules['forbidden-patterns'] || '') +
24
+ '\n' + lines.filter(l => l.startsWith('- ')).join('\n');
25
+ }
26
+ }
27
+ }
28
+
29
+ return rules;
30
+ }
31
+
32
+ module.exports = { loadAdapterRules };
@@ -0,0 +1,137 @@
1
+ // ai-native core — config reader
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+
7
+ function getProjectRoot() {
8
+ try {
9
+ return execSync('git rev-parse --show-toplevel', { encoding: 'utf-8' }).trim();
10
+ } catch {
11
+ return process.cwd();
12
+ }
13
+ }
14
+
15
+ function loadConfig(projectRoot) {
16
+ const configPath = path.join(projectRoot, '.ai-native', 'config.toml');
17
+ if (!fs.existsSync(configPath)) {
18
+ return getDefaultConfig();
19
+ }
20
+ return parseSimpleToml(fs.readFileSync(configPath, 'utf-8'));
21
+ }
22
+
23
+ function getDefaultConfig() {
24
+ return {
25
+ project: { name: 'unknown', type: 'frontend' },
26
+ stack: { adapter: 'react-spa', package_manager: 'pnpm' },
27
+ paradigm: { watch_paths: [] },
28
+ hooks: { enabled: true, reminder_cooldown_sec: 1200 },
29
+ sdd: { enforced: true },
30
+ onboarding: { demo_enabled: false },
31
+ distiller: { manifest: '.ai-native/memory/MANIFEST.yaml', output_dir: '.ai-native/memory/' },
32
+ };
33
+ }
34
+
35
+ function parseSimpleToml(content) {
36
+ const config = getDefaultConfig();
37
+ const lines = content.split('\n');
38
+ let currentSection = null;
39
+
40
+ for (const line of lines) {
41
+ const trimmed = line.trim();
42
+ if (!trimmed || trimmed.startsWith('#')) continue;
43
+
44
+ const sectionMatch = trimmed.match(/^\[(.+?)\]/);
45
+ if (sectionMatch) {
46
+ currentSection = sectionMatch[1];
47
+ continue;
48
+ }
49
+
50
+ const kvMatch = trimmed.match(/^(\S+)\s*=\s*(.+)/);
51
+ if (kvMatch && currentSection) {
52
+ const key = kvMatch[1];
53
+ let value = kvMatch[2].trim();
54
+
55
+ if (value === 'true') value = true;
56
+ else if (value === 'false') value = false;
57
+ else if (/^\d+$/.test(value)) value = parseInt(value);
58
+ else if (value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1);
59
+ else if (value.startsWith('[')) {
60
+ try { value = JSON.parse(value); } catch { value = [value]; }
61
+ }
62
+
63
+ const parts = currentSection.split('.');
64
+ let obj = config;
65
+ for (const part of parts) {
66
+ if (!obj[part]) obj[part] = {};
67
+ obj = obj[part];
68
+ }
69
+ if (Array.isArray(obj)) {
70
+ obj.push(value);
71
+ } else {
72
+ obj[key] = value;
73
+ }
74
+ }
75
+ }
76
+ return config;
77
+ }
78
+
79
+ function loadManifest(projectRoot) {
80
+ const config = loadConfig(projectRoot);
81
+ const manifestPath = path.join(projectRoot, config.distiller?.manifest || '.ai-native/memory/MANIFEST.yaml');
82
+ if (!fs.existsSync(manifestPath)) {
83
+ return { factors: [] };
84
+ }
85
+ const content = fs.readFileSync(manifestPath, 'utf-8');
86
+ return parseSimpleYaml(content);
87
+ }
88
+
89
+ function parseSimpleYaml(content) {
90
+ const result = { factors: [] };
91
+ const lines = content.split('\n');
92
+ let inFactors = false;
93
+ let currentFactor = null;
94
+ let inBuiltinSources = false;
95
+ let collectingList = null;
96
+
97
+ for (const line of lines) {
98
+ const trimmed = line.trim();
99
+ if (!trimmed || trimmed.startsWith('#')) continue;
100
+
101
+ if (trimmed === 'factors:') { inFactors = true; continue; }
102
+
103
+ if (inFactors) {
104
+ if (trimmed.startsWith('- name:')) {
105
+ if (currentFactor) result.factors.push(currentFactor);
106
+ currentFactor = { name: trimmed.replace('- name:', '').trim(), source_globs: [], builtin_sources: [] };
107
+ collectingList = null;
108
+ inBuiltinSources = false;
109
+ } else if (currentFactor) {
110
+ if (trimmed.startsWith('source_globs:')) {
111
+ collectingList = 'source_globs';
112
+ const value = trimmed.replace('source_globs:', '').trim();
113
+ if (value && value !== '[]') currentFactor.source_globs.push(value.replace(/^- /, '').replace(/"/g, ''));
114
+ } else if (trimmed.startsWith('builtin_sources:')) {
115
+ collectingList = 'builtin_sources';
116
+ inBuiltinSources = true;
117
+ const value = trimmed.replace('builtin_sources:', '').trim();
118
+ if (value && value !== '[]') currentFactor.builtin_sources.push(value.replace(/^- /, '').replace(/"/g, ''));
119
+ } else if (trimmed.startsWith('- ') && collectingList) {
120
+ const value = trimmed.replace(/^- /, '').replace(/"/g, '');
121
+ currentFactor[collectingList].push(value);
122
+ } else if (trimmed.includes(':')) {
123
+ const [k, v] = trimmed.split(':').map(s => s.trim());
124
+ if (k === 'output_file') currentFactor.output_file = v.replace(/"/g, '');
125
+ else if (k === 'max_items') currentFactor.max_items = parseInt(v);
126
+ else if (k === 'priority') currentFactor.priority = v;
127
+ else if (k === 'description') currentFactor.description = v;
128
+ collectingList = null;
129
+ }
130
+ }
131
+ }
132
+ }
133
+ if (currentFactor) result.factors.push(currentFactor);
134
+ return result;
135
+ }
136
+
137
+ module.exports = { getProjectRoot, loadConfig, loadManifest };