feat: integrate learning module
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
inman
2026-08-16 20:52:16 +08:00
parent 26b52d76e3
commit 01bee3188b
107 changed files with 6318 additions and 12063 deletions

View File

@@ -29,6 +29,7 @@ const RETIRED_COURSE_SKILL_IDS = [
'designer-design-spec',
'dev-build-test',
'game-assets',
'game-engine',
'marketing-launch-story',
'nianxxgame-skill',
'partner-agent-showcase',

View File

@@ -13,6 +13,7 @@ import {
import { createPublishableProjectTemplate } from './project-template';
export const PROJECT_CONFIG_PATH = '.niancode/project.json';
const RETIRED_PROJECT_SKILL_IDS = new Set(['game-engine']);
function configPath(projectPath: string): string {
return path.join(projectPath, PROJECT_CONFIG_PATH);
@@ -26,6 +27,10 @@ function normalizeStringList(value: unknown): string[] {
.filter(Boolean))];
}
function normalizeProjectSkillIds(value: unknown): string[] {
return normalizeStringList(value).filter((skillId) => !RETIRED_PROJECT_SKILL_IDS.has(skillId));
}
function normalizeProjectType(value: unknown): ProjectType {
if (value === undefined) return 'custom';
if (isProjectType(value)) return value;
@@ -70,7 +75,7 @@ function normalizeAgent(value: unknown): ProjectAgentConfig | null {
builtIn: raw.builtIn === true,
enabled: raw.enabled !== false,
model: typeof raw.model === 'string' && raw.model.trim() ? raw.model.trim() : null,
skillIds: normalizeStringList(raw.skillIds),
skillIds: normalizeProjectSkillIds(raw.skillIds),
responsibility: {
mission: typeof responsibility.mission === 'string' ? responsibility.mission.trim() : '',
owns: normalizeStringList(responsibility.owns),
@@ -86,6 +91,17 @@ function normalizeAgent(value: unknown): ProjectAgentConfig | null {
};
}
function needsRetiredProjectSkillMigration(value: unknown): boolean {
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
const raw = value as Partial<ProjectConfig>;
if (!Array.isArray(raw.agents)) return false;
return raw.agents.some((agent) => {
if (!agent || typeof agent !== 'object' || Array.isArray(agent)) return false;
const skillIds = (agent as { skillIds?: unknown }).skillIds;
return normalizeStringList(skillIds).some((skillId) => RETIRED_PROJECT_SKILL_IDS.has(skillId));
});
}
export type ProjectConfigReadResult =
| { status: 'valid'; config: ProjectConfig }
| { status: 'missing' }
@@ -138,7 +154,7 @@ export async function readProjectConfig(projectPath: string): Promise<ProjectCon
if (!(await areMaterializedAgentsCurrent(projectPath, config))) {
await materializeAgents(projectPath, config);
}
if (needsLegacyAgentModelMigration(raw)) {
if (needsLegacyAgentModelMigration(raw) || needsRetiredProjectSkillMigration(raw)) {
await writeFile(configPath(projectPath), `${JSON.stringify(config, null, 2)}\n`, 'utf8');
}
return { status: 'valid', config };
@@ -193,7 +209,7 @@ export function buildProjectAgentPrompt(config: ProjectConfig, current: ProjectA
function buildAgentMarkdown(config: ProjectConfig, agent: ProjectAgentConfig): string {
const skills = [
' "*": deny',
...agent.skillIds
...normalizeProjectSkillIds(agent.skillIds)
.filter((skill) => skill !== '*')
.map((skill) => ` ${skill}: allow`),
].join('\n');
@@ -232,7 +248,7 @@ function buildSelectedSkillGuidance(skillIds: string[]): string {
if (skillIds.includes('planning-with-files')) {
guidance.push([
'## 自动调用:项目规划',
'当任务包含多个阶段、需要研究或可能跨会话继续时,主动调用 `planning-with-files`,并按该技能在 `.niancode/agent-planning/` 中维护规划文件。',
'当任务包含多个阶段、需要研究或可能跨会话继续时,主动调用 `planning-with-files`,并按该技能在当前项目根目录直接维护 `task_plan.md`、`findings.md` 和 `progress.md`;使用当前会话项目目录的绝对路径,不要写入 Skill 安装目录、进程工作目录、用户目录或 `.niancode/agent-planning/`。',
].join('\n'));
}
return guidance.join('\n\n');