完善客户端模块与工作区能力
This commit is contained in:
@@ -21,6 +21,12 @@ export interface EnsureBundledCourseSkillsOptions {
|
||||
|
||||
export const BUNDLED_COURSE_SKILL_IDS = [
|
||||
'agent-browser',
|
||||
] as const;
|
||||
const RETIRED_COURSE_SKILL_IDS = [
|
||||
'course-stage-review',
|
||||
'student-growth-logger',
|
||||
'ai-video-creation-skill',
|
||||
'deploy-publish-check',
|
||||
'designer-design-spec',
|
||||
'dev-build-test',
|
||||
'game-assets',
|
||||
@@ -30,13 +36,8 @@ export const BUNDLED_COURSE_SKILL_IDS = [
|
||||
'pm-project-plan',
|
||||
'product-demo-prototype',
|
||||
'ui-ux-course-quality',
|
||||
'youth-plain-language',
|
||||
'youth-ai-product-course',
|
||||
] as const;
|
||||
const RETIRED_COURSE_SKILL_IDS = [
|
||||
'course-stage-review',
|
||||
'deploy-publish-check',
|
||||
'student-growth-logger',
|
||||
'youth-plain-language',
|
||||
] as const;
|
||||
const LEGACY_SUPERPOWERS_ARTIFACTS = [
|
||||
'plugins/superpowers-niancode.js',
|
||||
|
||||
@@ -2,6 +2,7 @@ import path from 'node:path';
|
||||
import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
|
||||
import {
|
||||
createProjectConfig,
|
||||
isProjectAgentAvatarDataUrl,
|
||||
isProjectType,
|
||||
validateAgentConfigs,
|
||||
validateAgentNames,
|
||||
@@ -61,6 +62,7 @@ function normalizeAgent(value: unknown): ProjectAgentConfig | null {
|
||||
avatarId: typeof raw.avatarId === 'string' && /^avatar-(0[1-9]|1[0-6])$/.test(raw.avatarId)
|
||||
? raw.avatarId
|
||||
: 'avatar-01',
|
||||
avatarDataUrl: isProjectAgentAvatarDataUrl(raw.avatarDataUrl) ? raw.avatarDataUrl : undefined,
|
||||
roleName: typeof raw.roleName === 'string' && raw.roleName.trim() ? raw.roleName.trim() : '项目伙伴',
|
||||
name,
|
||||
// Preserve this legacy marker when reading an existing project. New
|
||||
@@ -133,8 +135,10 @@ export async function readProjectConfig(projectPath: string): Promise<ProjectCon
|
||||
try {
|
||||
const raw = JSON.parse(await readFile(configPath(projectPath), 'utf8')) as unknown;
|
||||
const config = normalizeProjectConfig(raw);
|
||||
if (!(await areMaterializedAgentsCurrent(projectPath, config))) {
|
||||
await materializeAgents(projectPath, config);
|
||||
}
|
||||
if (needsLegacyAgentModelMigration(raw)) {
|
||||
if (config.initialized) await materializeAgents(projectPath, config);
|
||||
await writeFile(configPath(projectPath), `${JSON.stringify(config, null, 2)}\n`, 'utf8');
|
||||
}
|
||||
return { status: 'valid', config };
|
||||
@@ -187,9 +191,12 @@ export function buildProjectAgentPrompt(config: ProjectConfig, current: ProjectA
|
||||
}
|
||||
|
||||
function buildAgentMarkdown(config: ProjectConfig, agent: ProjectAgentConfig): string {
|
||||
const skills = agent.skillIds.length > 0
|
||||
? agent.skillIds.map((skill) => ` ${skill}: allow`).join('\n')
|
||||
: ' "*": deny';
|
||||
const skills = [
|
||||
' "*": deny',
|
||||
...agent.skillIds
|
||||
.filter((skill) => skill !== '*')
|
||||
.map((skill) => ` ${skill}: allow`),
|
||||
].join('\n');
|
||||
const shellPermission = agent.skillIds.includes('game-assets') ? ' bash: allow\n' : '';
|
||||
const model = agent.model;
|
||||
const prompt = agent.prompt.trim() || buildProjectAgentPrompt(config, agent);
|
||||
@@ -205,6 +212,21 @@ ${skills}
|
||||
${prompt}`;
|
||||
}
|
||||
|
||||
async function areMaterializedAgentsCurrent(projectPath: string, config: ProjectConfig): Promise<boolean> {
|
||||
if (!config.initialized || config.agents.length === 0) return true;
|
||||
|
||||
const results = await Promise.all(config.agents.map(async (agent) => {
|
||||
try {
|
||||
const existing = await readFile(path.join(projectPath, '.opencode', 'agent', `${agent.id}.md`), 'utf8');
|
||||
return existing === buildAgentMarkdown(config, agent);
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false;
|
||||
throw error;
|
||||
}
|
||||
}));
|
||||
return results.every(Boolean);
|
||||
}
|
||||
|
||||
async function materializeAgents(projectPath: string, config: ProjectConfig): Promise<void> {
|
||||
const agentDirectory = path.join(projectPath, '.opencode', 'agent');
|
||||
await mkdir(agentDirectory, { recursive: true });
|
||||
|
||||
@@ -66,9 +66,7 @@ export interface OpencodeProviderModelEntry {
|
||||
export interface OpencodeRuntimeConfig {
|
||||
'$schema': string;
|
||||
enabled_providers: string[];
|
||||
permission: {
|
||||
external_directory: 'ask';
|
||||
};
|
||||
permission: 'allow';
|
||||
provider: Record<string, OpencodeProviderEntry>;
|
||||
mcp?: Record<string, OpencodeMcpServerEntry>;
|
||||
model?: string;
|
||||
@@ -283,9 +281,7 @@ export async function buildOpencodeRuntimeConfig(
|
||||
const config: OpencodeRuntimeConfig = {
|
||||
'$schema': OPENCODE_CONFIG_SCHEMA,
|
||||
enabled_providers: [],
|
||||
permission: {
|
||||
external_directory: 'ask',
|
||||
},
|
||||
permission: 'allow',
|
||||
provider: {},
|
||||
};
|
||||
if (options.mcpServers && Object.keys(options.mcpServers).length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user