feat: integrate learning module
This commit is contained in:
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user