feat: add bundled OpenCode skills and refine module flow
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-14 18:15:55 +08:00
parent 88f9ee8708
commit c809002180
31 changed files with 1153 additions and 241 deletions

View File

@@ -219,6 +219,39 @@ const courseSkills: Skill[] = [
appliesTo: ['需要网页调试的项目伙伴'],
statusLabel: '平台内置 · 可手动选择',
},
{
id: 'grilling',
name: '方案质询',
description: '在重要工作开始前逐项澄清目标、范围和关键取舍,确认后再执行。',
prompt: '当任务涉及重要实现或设计决策时,先逐项确认关键选择,不要在未确认前修改项目。',
inputs: ['用户目标', '范围与约束', '验收标准'],
outputs: ['已确认的方案', '主要取舍', '剩余不确定性'],
guardrails: ['每轮只问一个问题', '先查代码事实', '确认前不执行实现'],
appliesTo: ['需要澄清复杂需求的项目伙伴'],
statusLabel: '平台内置 · 默认勾选',
},
{
id: 'planning-with-files',
name: '项目规划',
description: '为复杂任务维护可恢复的计划、发现和进展,记录在项目的 .niancode/agent-planning/ 中。',
prompt: '复杂任务开始前在 .niancode/agent-planning/ 建立并持续更新 task_plan.md、findings.md 和 progress.md。',
inputs: ['复杂任务目标', '阶段与成功标准', '代码和验证结果'],
outputs: ['可恢复的计划', '研究发现', '进展与验证记录'],
guardrails: ['简单任务不创建规划文件', '不记录密钥和敏感数据', '遵守项目 AGENTS.md'],
appliesTo: ['需要持续推进复杂任务的项目伙伴'],
statusLabel: '平台内置 · 默认勾选',
},
{
id: 'frontend-slides',
name: '项目演示',
description: '把项目内容整理成可播放的 16:9 HTML 幻灯片,不生成 PPTX 或云端发布。',
prompt: '当用户准备汇报、结题展示或项目介绍时,主动把真实项目内容整理成可播放的本地 HTML 幻灯片,并用浏览器检查布局和素材。',
inputs: ['项目目标与受众', '功能截图或素材', '想讲清楚的过程与结果'],
outputs: ['16:9 HTML 演示', '风格草图', '浏览器验收结果'],
guardrails: ['只生成本地 HTML', '不生成 PPTX', '不部署或上传到云端', '不编造项目数据'],
appliesTo: ['需要结题展示的项目伙伴'],
statusLabel: '平台内置 · 可手动选择',
},
];
const knowledgeFiles: KnowledgeFile[] = [

View File

@@ -1,4 +1,3 @@
import { ArrowRight } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import moduleCanvasImage from '@/assets/module-canvas-neon.jpg';
import moduleGameImage from '@/assets/module-game.jpg';
@@ -70,7 +69,8 @@ export function ModuleSelection({ authRequired = true }: { authRequired?: boolea
!module.enabled && 'module-option-card-disabled',
)}
>
<div className="module-option-card-surface relative z-10 flex h-full w-full overflow-hidden rounded-lg bg-background">
<span className="module-option-card-frame" aria-hidden="true" />
<div className="module-option-card-surface relative flex h-full w-full rounded-lg bg-background">
<div className="module-option-card-image relative shrink-0">
<img
src={content.image}
@@ -78,18 +78,14 @@ export function ModuleSelection({ authRequired = true }: { authRequired?: boolea
className={cn('module-option-card-image-media h-full w-full object-cover', !module.enabled && 'grayscale')}
draggable="false"
/>
{module.enabled ? (
<span className="module-option-card-arrow absolute flex items-center justify-center rounded-full bg-background/90 text-foreground shadow-sm">
<ArrowRight className="module-option-card-arrow-icon" strokeWidth={2.6} />
</span>
) : null}
</div>
<div className="module-option-card-content min-h-0 flex-1">
<div className="module-option-card-content relative min-h-0 flex-1">
<p className="module-option-card-title font-medium tracking-[-0.02em]">{content.label}</p>
<p className="module-option-card-description font-medium text-muted-foreground">{module.description}</p>
{!module.enabled ? <p className="module-option-card-status font-semibold">暂未开放</p> : null}
</div>
</div>
<span className="module-option-card-hit-area" aria-hidden="true" />
</button>
);
})}

View File

@@ -87,8 +87,8 @@ function DrawerFrame({ title, desc, onClose, children, closeLabel = '关闭', cl
.from('.drawer-motion-header', { x: 8, duration: 0.22 })
.from('.drawer-motion-body > *', { y: 6, duration: 0.22, stagger: 0.035 }, '-=0.12');
}, { scope: drawerRef });
const closeButton = <Button type="button" variant="ghost" size="icon" onClick={onClose} aria-label={closeLabel} title={closeLabel}>{closeIcon === 'back' ? <ArrowLeft className="h-4 w-4" /> : <X className="h-4 w-4" />}</Button>;
return <div ref={drawerRef} className="flex min-h-0 flex-1 flex-col"><div className="glass-surface drawer-motion-header flex items-start justify-between gap-3 border-b border-border/70 bg-background/80 px-5 py-4"><div><SheetTitle className="text-lg font-semibold">{title}</SheetTitle><SheetDescription className="mt-1 text-sm font-medium text-muted-foreground">{desc}</SheetDescription></div>{closeIcon === 'back' ? closeButton : <SheetClose asChild>{closeButton}</SheetClose>}</div><div className="drawer-motion-body min-h-0 flex-1 overflow-auto p-5">{children}</div></div>;
const closeButton = <Button type="button" variant="ghost" size="icon" onClick={onClose} aria-label={closeLabel} title={closeLabel} className="relative z-10 shrink-0">{closeIcon === 'back' ? <ArrowLeft className="h-4 w-4" /> : <X className="h-4 w-4" />}</Button>;
return <div ref={drawerRef} className="flex min-h-0 flex-1 flex-col"><div className="glass-surface drawer-motion-header flex items-center justify-between gap-3 border-b border-border/70 bg-background/80 px-5 py-4"><div className="min-w-0 flex-1"><SheetTitle className="text-lg font-semibold">{title}</SheetTitle><SheetDescription className="mt-1 text-sm font-medium text-muted-foreground">{desc}</SheetDescription></div>{closeIcon === 'back' ? closeButton : <SheetClose asChild>{closeButton}</SheetClose>}</div><div className="drawer-motion-body min-h-0 flex-1 overflow-auto p-5">{children}</div></div>;
}
function SkillDetail({ skill }: { skill: SkillInfo }) {