Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const { loadSkillsMock, searchMock } = vi.hoisted(() => ({

vi.mock("@avenire/ai/skills", () => ({
AVAILABLE_STUDY_SKILLS: ["explain", "summarize"],
AVAILABLE_TEACHING_SKILLS: [],
AVAILABLE_VISUAL_SKILLS: ["diagram", "chart"],
loadSkills: loadSkillsMock,
}));
Expand Down
61 changes: 60 additions & 1 deletion apps/web/src/lib/chat-tools/chat-tool-utility-runtime.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import {
AVAILABLE_STUDY_SKILLS,
AVAILABLE_TEACHING_SKILLS,
AVAILABLE_VISUAL_SKILLS,
loadSkills,
} from "@avenire/ai/skills";
import {
getTeachingArtifacts,
readTeachingArtifact,
saveTeachingArtifact,
type TeachingArtifactKind,
} from "@avenire/database";
import { Firecrawl } from "firecrawl";
import { z } from "zod";

Expand Down Expand Up @@ -48,6 +55,15 @@ type WebSearchInput = z.infer<
type LoadSkillInput = z.infer<
typeof import("@avenire/ai/tools").chatToolSchemas["load_skill"]["input"]
>;
type TeachingWorkspaceInput = z.infer<
typeof import("@avenire/ai/tools").chatToolSchemas["get_teaching_workspace"]["input"]
>;
type SaveTeachingArtifactInput = z.infer<
typeof import("@avenire/ai/tools").chatToolSchemas["save_teaching_artifact"]["input"]
>;
type ReadTeachingArtifactInput = z.infer<
typeof import("@avenire/ai/tools").chatToolSchemas["read_teaching_artifact"]["input"]
>;
type VisualizeReadMeInput = z.infer<
typeof import("@avenire/ai/tools").chatToolSchemas["visualize_read_me"]["input"]
>;
Expand Down Expand Up @@ -131,7 +147,9 @@ export async function executeLoadSkill(input: LoadSkillInput) {
new Set(
input.skills.flatMap((skillName) => {
const normalizedName = skillName.trim();
return AVAILABLE_STUDY_SKILLS.some((skill) => skill === normalizedName)
return [...AVAILABLE_STUDY_SKILLS, ...AVAILABLE_TEACHING_SKILLS].some(
(skill) => skill === normalizedName
)
? [normalizedName]
: [];
})
Expand All @@ -146,6 +164,47 @@ export async function executeLoadSkill(input: LoadSkillInput) {
};
}

export async function executeGetTeachingWorkspace(
ctx: { userId: string; workspaceId: string },
input: TeachingWorkspaceInput
) {
return getTeachingArtifacts({
...input,
kind: input.kind as TeachingArtifactKind | undefined,
userId: ctx.userId,
workspaceId: ctx.workspaceId,
});
}

export async function executeReadTeachingArtifact(
ctx: { userId: string; workspaceId: string },
input: ReadTeachingArtifactInput
) {
const artifact = await readTeachingArtifact({
...input,
kind: input.kind as TeachingArtifactKind,
userId: ctx.userId,
workspaceId: ctx.workspaceId,
});
return { artifact };
}

export async function executeSaveTeachingArtifact(
ctx: { userId: string; workspaceId: string },
input: SaveTeachingArtifactInput
) {
const artifact = await saveTeachingArtifact({
...input,
kind: input.kind as TeachingArtifactKind,
userId: ctx.userId,
workspaceId: ctx.workspaceId,
});
return {
artifact,
summary: `Saved ${artifact.kind} "${artifact.title}" in the private teaching workspace.`,
};
}

export async function executeVisualizeReadMe(input: VisualizeReadMeInput) {
const modules = Array.from(
new Set(
Expand Down
26 changes: 25 additions & 1 deletion apps/web/src/lib/chat-tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import {
} from "@/lib/chat-tools/chat-tool-study-runtime";
import {
executeLoadSkill,
executeGetTeachingWorkspace,
executeReadTeachingArtifact,
executeSaveTeachingArtifact,
executeShowWidgetWithOptions,
executeVisualizeReadMe,
runWebSearch,
Expand Down Expand Up @@ -247,11 +250,32 @@ export function createChatTools(ctx: ChatToolContext): ToolSet {
}),
load_skill: tool({
description:
"Load a study-guideline skill into context. Use this before acting on structured study tasks like explanations, summaries, notes, mindset cards, or quizzes.",
"Load a study or teaching skill into context before acting on a matching task.",
inputSchema: toolSchema(chatToolSchemas.load_skill.input),
outputSchema: toolSchema(chatToolSchemas.load_skill.output),
execute: async (input) => executeLoadSkill(input),
}),
get_teaching_workspace: tool({
description:
"Read the private teaching workspace for this user and workspace. Use it before teaching, planning a learning path, or running a learning retrospective. This state is not a visible workspace file.",
inputSchema: toolSchema(chatToolSchemas.get_teaching_workspace.input),
outputSchema: toolSchema(chatToolSchemas.get_teaching_workspace.output),
execute: async (input) => executeGetTeachingWorkspace(ctx, input),
}),
read_teaching_artifact: tool({
description:
"Read one full teaching artifact (mission, resource, note, reference, lesson, or learning-record) by kind and slug after get_teaching_workspace identifies it. Use for targeted content reads.",
inputSchema: toolSchema(chatToolSchemas.read_teaching_artifact.input),
outputSchema: toolSchema(chatToolSchemas.read_teaching_artifact.output),
execute: async (input) => executeReadTeachingArtifact(ctx, input),
}),
save_teaching_artifact: tool({
description:
"Save mission, resource, note, reference, lesson, or learning-record content to the private teaching workspace. Use explicit complete content; do not create visible workspace files for teaching state.",
inputSchema: toolSchema(chatToolSchemas.save_teaching_artifact.input),
outputSchema: toolSchema(chatToolSchemas.save_teaching_artifact.output),
execute: async (input) => executeSaveTeachingArtifact(ctx, input),
}),
visualize_read_me: tool({
description:
"Load visualization guidelines for widget generation. Call this before generating widgets to get detailed instructions for interactive HTML/CSS/SVG fragments.",
Expand Down
12 changes: 6 additions & 6 deletions packages/ai/prompts/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ export function APOLLO_PROMPT(
: context;
return [
`You are Avenire AI assistant${userName ? ` for ${userName}` : ""}.`,
"Keep responses concise, correct, and helpful.",
"Answer directly. Be concise, specific, and correct.",
"When you write math, always use LaTeX delimiters in normal text: inline math with $...$ and display math with $$...$$ if needed. Never wrap math in ```latex fences.",
"Default to general knowledge; do not access workspace tools unless the user explicitly asks about their files/workspace or the request is too niche to answer without personal context.",
"If the topic is niche or lacks context, ask a brief clarification first; only explore the workspace if the user confirms or references their files.",
"Default to general knowledge; do not access workspace tools unless the user explicitly asks about their files/workspace, the request is too niche to answer without personal context, or the request is for teaching.",
"If a niche request lacks context, ask one brief clarification. Search the workspace only when the user asks about it or confirms that personal context is needed.",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"Use the avenire_agent tool for workspace retrieval (searching, reading, summarizing files) only when the above conditions apply.",
"When workspace retrieval tools return citations or citationMarkdown, cite workspace-derived factual claims in your final answer.",
"Use this exact citation format for workspace sources: [workspace/path.ext](workspace-file://<fileId>).",
Expand Down Expand Up @@ -92,10 +92,10 @@ export function APOLLO_PROMPT(
"Use topic-scoped due-card checks when possible by passing the relevant subject, topic, and concept to get_due_cards.",
"Only generate flashcards or quizzes when the user explicitly asks for them or provides study material for that purpose.",
"When creating flashcards for a topic or misconception, prefer extending the existing deck for that same topic instead of creating a duplicate deck.",
"When a request clearly matches a study-guideline skill, call load_skill first to fetch the matching instructions into context before acting.",
"Study-oriented skills available through load_skill include `concept-explainer`, `summary-generator`, `study-notes-creator`, `flashcard-creator`, and `quiz-creator`.",
"When a request matches a skill, call load_skill before acting. Study skills include `concept-explainer`, `summary-generator`, `study-notes-creator`, `flashcard-creator`, and `quiz-creator`. Teaching skills include `teach`, `create-learning-path`, and `run-learning-retrospective`.",
"Teaching state is private application data. Before teaching, read `get_teaching_workspace`; save mission, resources, references, lessons, notes, and learning records with `save_teaching_artifact`. Do not create visible workspace files for teaching state.",
'For widgets, use `type: "spec"` when the existing primitives clearly fit; use `type: "code"` for custom SVG/canvas, controls, animation, simulations, maps, or unsupported chart types. The schema path is optional: choose the mode from the artifact, not from response length.',
"When you decide to use show_widget, call the tool directly with the complete final `widget` payload. Do not write user-visible narration about building JSON, trying again, missing schema fields, empty code, tool formatting, or what you are about to send to the tool.",
"When using show_widget, call it directly with the complete final `widget` payload. Do not narrate tool payload construction or retries.",
allowVisualizations
? "Use show_widget when a visual makes comparison, process, trend, or dense data easier to scan. Call visualize_read_me first with only the needed modules, then set `i_have_seen_read_me: true`. Route `diagram` to mostly-static SVG structures, `chart` to quantitative data, `interactive` to controls/steppers/calculators, `physics` to simulations, `mockup` to app-like UI, and `art` to non-analytical illustration. Prefer `spec` when the primitives clearly fit; use `code` for custom drawing or interaction. Lead with the key signal, add only relevant controls, and keep labels short. Use three.js only for real 3D."
: "Do not use show_widget or visualize_read_me in this conversation. You may still use load_skill for study-guideline skills when helpful.",
Expand Down
20 changes: 16 additions & 4 deletions packages/ai/skills/scripts/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { readdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

type SkillSection = "study-guidelines" | "visual-guidelines";
type SkillSection =
| "agent-guidelines"
| "study-guidelines"
| "visual-guidelines";

type ParsedFrontmatter = {
attributes: {
Expand Down Expand Up @@ -198,14 +201,19 @@ async function main() {
.filter((entry) => entry.section === "study-guidelines")
.map((entry) => entry.id)
.sort();
const teachingSkillIds = entries
.filter((entry) => entry.section === "agent-guidelines")
.map((entry) => entry.id)
.sort();
const lines: string[] = [];

lines.push("// This file is generated. Do not edit by hand.");
lines.push("// Run: bun packages/ai/skills/scripts/generator.ts");
lines.push("");
lines.push(
'export type SkillSection = "study-guidelines" | "visual-guidelines";'
);
lines.push('export type SkillSection =');
lines.push(' | "agent-guidelines"');
lines.push(' | "study-guidelines"');
lines.push(' | "visual-guidelines";');
lines.push("");
lines.push("export type SkillDefinition = {");
lines.push(" id: string;");
Expand Down Expand Up @@ -241,6 +249,10 @@ async function main() {
`export const AVAILABLE_STUDY_SKILLS = ${JSON.stringify(studySkillIds)} as const;`
);
lines.push("");
lines.push(
`export const AVAILABLE_TEACHING_SKILLS = ${JSON.stringify(teachingSkillIds)} as const;`
);
lines.push("");
lines.push(
`export const AVAILABLE_VISUAL_SKILLS = ${JSON.stringify(visualSkillIds)} as const;`
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: create-learning-path
description: Build a personalized learning roadmap with milestones and practice checkpoints.
---

# Create a learning path

Read the private teaching workspace with `get_teaching_workspace` first. Save the resulting roadmap as a `reference` or `lesson` artifact, not as a visible workspace file.

1. Assess the learner's baseline and target outcome.
2. Sequence the smallest set of topics from fundamentals to applied practice.
3. Define achievable milestones, practice tasks, feedback criteria, and time-boxed checkpoints.
4. Include reflection in every phase and limit the resource list.
5. End with a measurable next milestone.

Completion means the roadmap names the target, order, practice, feedback, timing, and next checkpoint.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: run-learning-retrospective
description: Review learning progress, find blockers, and adjust the next milestone.
---

# Run a learning retrospective

Read the private teaching workspace with `get_teaching_workspace` first. Save the retrospective as a `learning-record` artifact, not as a visible workspace file.

1. Compare completed work with the target outcomes.
2. Identify recurring blockers and weak concepts from evidence, not impressions.
3. Decide what to reinforce, what to defer, and why.
4. Adjust pacing and practice tasks.
5. Set one next milestone with a measurable checkpoint.

Completion means the retrospective records progress, blockers, changes, and the next checkpoint.
25 changes: 25 additions & 0 deletions packages/ai/skills/sections/agent-guidelines/teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
name: teach
description: Teach a user a skill or concept across sessions with short lessons and practice. Use when the user asks to learn.
---

# Teach

Use the private teaching workspace. Do not store teaching state in visible workspace files.

## Before teaching

1. Call `get_teaching_workspace` before teaching. Read the mission, learning records, notes, references, and resources it returns.
2. If the mission is missing or unclear, ask why the learner wants this and what outcome they need. Do not invent a mission.
3. Use trusted sources for factual content. Save sources as `resource` artifacts.

## Lesson loop

1. Teach only the knowledge needed for one narrowly scoped skill.
2. Make the learner retrieve, apply, and receive feedback. Prefer a short interactive exercise.
3. Save the lesson as a `lesson` artifact with a stable slug and complete HTML content.
4. Save durable facts as `reference` artifacts and non-obvious progress as `learning-record` artifacts.
5. Save mission changes only after the learner confirms them. Use the `mission` artifact for the current mission.
6. End with a concrete next step and a way to ask follow-up questions.

Keep lessons short enough to finish quickly. Favor storage strength over a smooth explanation: recall, spacing, and interleaving matter more than immediate fluency.
33 changes: 33 additions & 0 deletions packages/ai/skills/sections/agent-guidelines/unslop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
name: unslop
description: Rewrite plans and documentation in direct, specific language. Use when removing generic AI prose.
---

# Unslop

Rewrite text so it sounds specific, direct, and human. Preserve meaning and the intended tone.

## Process

1. Scan for puffery, vague claims, filler, repetition, over-structured prose, and generic conclusions.
2. Replace each with a concrete fact, instruction, decision, example, or number.
3. Prefer active voice, plain words, varied sentence length, and sentence-case headings.
4. Keep the author's point of view when it helps. Do not add enthusiasm or marketing language.
5. Read the result once as a skeptical editor. Remove anything that could appear unchanged in another project's docs.

## Remove

- Puffery and promotion: "pivotal", "groundbreaking", "vibrant", "stunning", "must-visit", "testament".
- Vague attribution: name the source or remove the claim.
- Abstract metaphor jargon: "landscape", "flywheel", "north star", "bedrock", "scaffolding", "harness".
- Fancy verbs: use "is", "has", "use", and "help" instead of "serves as", "boasts", "leverage", and "facilitate".
- "Not just X, but Y", forced groups of three, synonym cycling, and false "from X to Y" ranges.
- Filler such as "in order to", "it is important to note", and excessive hedging.
- Chatbot phrases, sycophancy, decorative emojis, curly quotes, and em dashes.

## Keep

- One idea per sentence when a sentence becomes hard to parse.
- A single source of truth for each rule.
- Steps in execution order. End each step with a checkable completion condition.
- Reference material behind a clear pointer when only one branch needs it.
Loading
Loading