codecall 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,171 @@
1
+ # codecall
2
+
3
+ `codecall` is an agent-backed skill for Codex and Claude Code that turns a
4
+ completed implementation into a short, evidence-grounded, adaptive learning
5
+ session. It uses the active coding agent that already understands the task and
6
+ repository—there is no additional model call or external context upload.
7
+
8
+ The MVP deliberately teaches and checks understanding one step at a time:
9
+
10
+ ```text
11
+ collect minimal context → recommend → confidence → lesson → one check-in
12
+ ↑ ↓
13
+ └── reinforce / advance
14
+ ```
15
+
16
+ It does not throw a fixed quiz at the user. A normal session covers 2–4 related
17
+ concepts, using two checks per concept: a real-world-to-technical mapping and a
18
+ technical application or tradeoff question. A wrong answer produces targeted
19
+ reinforcement and a fresh equivalent check-in; correct answers unlock the next
20
+ prerequisite-ready learning step.
21
+
22
+ ## Install
23
+
24
+ Install once from GitHub to add the same `codecall` skill to both your local Codex
25
+ and Claude Code skills directories:
26
+
27
+ ```bash
28
+ npm install -g https://github.com/GAURAV-1313/codecall/archive/refs/heads/main.tar.gz
29
+ ```
30
+
31
+ Restart the relevant coding agent or open a new task so it reloads the installed
32
+ skill. This package does not require an additional API key, and its install step
33
+ does not upload your code.
34
+
35
+ When the package is published to npm, the equivalent registry command will be:
36
+
37
+ ```bash
38
+ npm install -g codecall
39
+ ```
40
+
41
+ ## Use it in Codex
42
+
43
+ After Codex completes an implementation, invoke:
44
+
45
+ ```text
46
+ $codecall
47
+ ```
48
+
49
+ The skill uses the existing conversation and the coding agent's recent edits.
50
+ It can optionally inspect a Git diff when that is useful, but Git is never a
51
+ requirement.
52
+ It first presents a Start/Skip recommendation. After Start, it teaches one
53
+ implementation-specific concept and asks one reasoning question at a time.
54
+ Your answer determines whether it reinforces, deepens, or advances. It finishes
55
+ with concepts covered, weak areas, and an estimated session mastery. The final
56
+ summary also gives implementation-grounded points to remember and only the
57
+ relevant edge cases to check before applying the pattern in another project.
58
+
59
+ The installable skill source is in [`skill/`](skill/SKILL.md).
60
+
61
+ ## Use it in Claude Code
62
+
63
+ After Claude Code completes an implementation, invoke:
64
+
65
+ ```text
66
+ /codecall
67
+ ```
68
+
69
+ Claude Code discovers personal skills from `~/.claude/skills/<skill-name>/SKILL.md`.
70
+ The installer creates `~/.claude/skills/codecall/SKILL.md`, using the same
71
+ Agent-Skills-standard instruction file as Codex. It uses Claude Code's current
72
+ conversation and edits—there is no extra learning-model API to configure.
73
+
74
+ For a repository-level automatic, non-blocking recommendation, add the same
75
+ rule below to `CLAUDE.md` rather than `AGENTS.md`.
76
+
77
+ ### Automatic recommendation
78
+
79
+ Codex and Claude Code can select the skill implicitly, but skills are not
80
+ background event listeners. `$codecall` (Codex) and `/codecall` (Claude Code) are the
81
+ dependable triggers in any repository.
82
+
83
+ The policy is intentionally selective: it recommends only when an implementation
84
+ has one strong signal (for example a security boundary, integration, public
85
+ contract, or architecture decision) or two moderate signals (such as a reusable
86
+ pattern plus operational behavior). It skips cosmetic, documentation-only,
87
+ mechanical, generated-only, dependency-only, and ordinary test-only work. The
88
+ full policy is [installed with the skill](skill/references/trigger-policy.md).
89
+
90
+ To enable it in a project, copy the [Codex template](skill/references/AGENTS.codecall.md)
91
+ into `AGENTS.md` or the [Claude Code template](skill/references/CLAUDE.codecall.md)
92
+ into `CLAUDE.md`:
93
+
94
+ ```md
95
+ Before the normal final response for a completed implementation, read the
96
+ codecall skill's references/trigger-policy.md and evaluate the change. Show Start
97
+ Learning / Skip only for a `recommend` outcome; never teach without Start.
98
+ ```
99
+
100
+ The policy retains only an in-memory concept fingerprint for the current agent
101
+ session, preventing duplicate cards without tracking learning across sessions
102
+ or projects.
103
+
104
+ ## Local demonstration runtime
105
+
106
+ ```bash
107
+ codecall --from-git --task "Add OAuth protected routes"
108
+ ```
109
+
110
+ The terminal command is a local, deterministic demonstration runtime. Use
111
+ `$codecall` in Codex for the primary agent-backed product experience. Use
112
+ `codecall --help` for command options.
113
+
114
+ ## Use it programmatically
115
+
116
+ ```ts
117
+ import { codecall } from "codecall";
118
+
119
+ const implementation = {
120
+ task: "Protect account routes using JWT authentication middleware.",
121
+ changedFiles: [{
122
+ path: "src/routes.ts",
123
+ summary: "Adds authentication middleware before account handlers."
124
+ }]
125
+ };
126
+
127
+ const runtime = codecall(implementation);
128
+ const session = await runtime.start(implementation);
129
+
130
+ runtime.decide(session, true); // developer chose Start Learning
131
+ await runtime.setConfidence(session, "heard_of_it");
132
+
133
+ // Render session.currentQuestion. On a user answer:
134
+ await runtime.answer(session, "causal");
135
+ ```
136
+
137
+ Providers render `runtime.history(session.id)` as their native UI: `/codecall`, a
138
+ tool call, or a non-blocking post-task recommendation. Core workers never
139
+ depend on a provider SDK.
140
+
141
+ ## What ships in v1.0.0
142
+
143
+ - append-only, typed session events and an explicit state machine;
144
+ - progressive minimal context represented as evidence references;
145
+ - explainable opportunity scoring based on concepts and architecture signals,
146
+ not LOC;
147
+ - separated concept categories, dependency-aware planning, and a bounded plan;
148
+ - an adaptive teach → one MCQ → evaluate → reinforce/advance loop;
149
+ - final transfer guidance: points to remember plus cross-project edge cases;
150
+ - one shared skill installed for Codex and Claude Code;
151
+ - an explainable strong/moderate auto-recommendation policy with session-local
152
+ duplicate suppression;
153
+ - pluggable worker and documentation-provider contracts;
154
+ - in-memory and caller-owned JSONL event stores;
155
+ - deterministic default workers suitable for local demonstration and tests.
156
+
157
+ The included deterministic workers support local tests and the terminal demo.
158
+ The installed agent skill is the production path: its reasoning, concept
159
+ selection, teaching, question generation, and evaluation are performed by the
160
+ active coding agent in the existing conversation.
161
+
162
+ ## Development
163
+
164
+ ```bash
165
+ npm install
166
+ npm run build
167
+ npm test
168
+ ```
169
+
170
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for the full design, privacy posture,
171
+ event model, state machine, and planned provider integrations.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env node
2
+ import { execFileSync } from "node:child_process";
3
+ import { readFileSync } from "node:fs";
4
+ import { createInterface } from "node:readline/promises";
5
+ import { stdin as input, stdout as output } from "node:process";
6
+ import { codecall } from "./public.js";
7
+ function parseArguments(values) {
8
+ const parsed = { fromGit: false, help: false };
9
+ for (let index = 0; index < values.length; index += 1) {
10
+ if (values[index] === "--task")
11
+ parsed.task = values[++index];
12
+ else if (values[index] === "--from-git")
13
+ parsed.fromGit = true;
14
+ else if (values[index] === "--help" || values[index] === "-h")
15
+ parsed.help = true;
16
+ else
17
+ throw new Error(`Unknown argument: ${values[index]}`);
18
+ }
19
+ return parsed;
20
+ }
21
+ function help() {
22
+ console.log(`Usage: codecall --task "Describe the implementation" [--from-git]
23
+
24
+ Starts an adaptive learning session. --from-git adds changed-file summaries and
25
+ small excerpts from the current working tree. For the full agent-backed
26
+ experience, invoke $codecall in Codex after completing an implementation.`);
27
+ }
28
+ function changedFilesFromGit() {
29
+ const options = { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] };
30
+ try {
31
+ // Porcelain status includes staged, unstaged, and newly-created files, even
32
+ // in a repository that has not created its first commit yet.
33
+ const names = execFileSync("git", ["status", "--porcelain"], options)
34
+ .split("\n")
35
+ .map((line) => line.slice(3).trim())
36
+ .map((path) => path.includes(" -> ") ? path.split(" -> ").at(-1) : path)
37
+ .filter(Boolean);
38
+ return names.slice(0, 12).map((path) => {
39
+ let excerpt;
40
+ try {
41
+ excerpt = readFileSync(path, "utf8").slice(0, 1_500);
42
+ }
43
+ catch { /* deleted or binary file */ }
44
+ return { path, summary: "Changed in the current Git diff.", excerpt };
45
+ });
46
+ }
47
+ catch {
48
+ return [];
49
+ }
50
+ }
51
+ function latest(events, type) {
52
+ return [...events].reverse().find((event) => event.type === type)?.payload;
53
+ }
54
+ function printLesson(lesson) {
55
+ console.log(`\n${lesson.title}\n${lesson.what}\n\nWhy here: ${lesson.whyHere}\nWhat breaks: ${lesson.failureMode}\nMental model: ${lesson.mentalModel}`);
56
+ }
57
+ async function main() {
58
+ const args = parseArguments(process.argv.slice(2));
59
+ if (args.help)
60
+ return help();
61
+ const rl = createInterface({ input, output });
62
+ try {
63
+ const task = args.task ?? await rl.question("What implementation did you complete? ");
64
+ const implementation = { task, changedFiles: args.fromGit ? changedFilesFromGit() : [] };
65
+ const runtime = codecall(implementation);
66
+ const session = await runtime.start(implementation);
67
+ const opportunity = session.opportunity;
68
+ console.log(`\nLearning opportunity: ${opportunity.recommendation} · ${opportunity.estimatedMinutes} minutes\n${opportunity.reasoning.join(" ")}`);
69
+ const accepted = /^(y|yes)$/i.test(await rl.question("Start learning? [y/N] "));
70
+ runtime.decide(session, accepted);
71
+ if (!accepted) {
72
+ console.log("Learning skipped.");
73
+ return;
74
+ }
75
+ const confidence = (await rl.question("Confidence [expert / comfortable / heard_of_it / never_learned]: ")).trim();
76
+ if (!["expert", "comfortable", "heard_of_it", "never_learned"].includes(confidence)) {
77
+ throw new Error("Choose expert, comfortable, heard_of_it, or never_learned.");
78
+ }
79
+ await runtime.setConfidence(session, confidence);
80
+ let renderedLessonCount = 0;
81
+ while (session.state === "quiz" && session.currentQuestion) {
82
+ const events = runtime.history(session.id);
83
+ const lessons = events.filter((event) => event.type === "LESSON_READY");
84
+ if (lessons.length > renderedLessonCount) {
85
+ printLesson(latest(events, "LESSON_READY"));
86
+ renderedLessonCount = lessons.length;
87
+ }
88
+ const question = session.currentQuestion;
89
+ console.log(`\n${question.prompt}`);
90
+ question.choices.forEach((choice, index) => console.log(` ${index + 1}. ${choice.text}`));
91
+ const selected = Number(await rl.question("Your answer: ")) - 1;
92
+ const choice = question.choices[selected];
93
+ if (!choice) {
94
+ console.log("Choose one of the displayed numbers.");
95
+ continue;
96
+ }
97
+ await runtime.answer(session, choice.id);
98
+ const feedback = latest(runtime.history(session.id), "ANSWER_EVALUATED");
99
+ if (feedback)
100
+ console.log(`\n${feedback.feedback}`);
101
+ }
102
+ if (session.summary)
103
+ console.log(`\nSession complete\nMastery: ${session.summary.estimatedSessionMastery}\nTakeaways:\n- ${session.summary.keyTakeaways.join("\n- ")}\nPoints to remember:\n- ${session.summary.pointsToRemember.join("\n- ")}\nEdge cases for other projects:\n- ${session.summary.edgeCasesForOtherProjects.join("\n- ")}`);
104
+ }
105
+ finally {
106
+ rl.close();
107
+ }
108
+ }
109
+ main().catch((error) => { console.error(error instanceof Error ? error.message : error); process.exitCode = 1; });
@@ -0,0 +1,4 @@
1
+ /** Copy the packaged skill into Codex's discoverable skill directory. */
2
+ export declare function installSkill(destinationRoot?: string): string;
3
+ /** Copy the same Agent Skills-standard skill into Claude Code's personal directory. */
4
+ export declare function installClaudeSkill(destinationRoot?: string): string;
@@ -0,0 +1,40 @@
1
+ import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ function packagedSkillSource() {
6
+ const here = dirname(fileURLToPath(import.meta.url));
7
+ return join(here, "..", "skill");
8
+ }
9
+ function copySkill(destinationRoot) {
10
+ const source = packagedSkillSource();
11
+ if (!existsSync(source))
12
+ throw new Error(`Packaged skill source was not found: ${source}`);
13
+ mkdirSync(destinationRoot, { recursive: true });
14
+ const destination = join(destinationRoot, "codecall");
15
+ cpSync(source, destination, { recursive: true, force: true });
16
+ const legacySkill = join(destinationRoot, "learn");
17
+ if (existsSync(legacySkill))
18
+ rmSync(legacySkill, { recursive: true, force: true });
19
+ return destination;
20
+ }
21
+ /** Copy the packaged skill into Codex's discoverable skill directory. */
22
+ export function installSkill(destinationRoot = process.env.CODEX_HOME ? join(process.env.CODEX_HOME, "skills") : join(homedir(), ".codex", "skills")) {
23
+ return copySkill(destinationRoot);
24
+ }
25
+ /** Copy the same Agent Skills-standard skill into Claude Code's personal directory. */
26
+ export function installClaudeSkill(destinationRoot = process.env.CLAUDE_CONFIG_DIR ? join(process.env.CLAUDE_CONFIG_DIR, "skills") : join(homedir(), ".claude", "skills")) {
27
+ return copySkill(destinationRoot);
28
+ }
29
+ const invokedAsScript = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1];
30
+ if (invokedAsScript) {
31
+ if (process.argv.includes("--global-only") && process.env.npm_config_global !== "true")
32
+ process.exit(0);
33
+ try {
34
+ console.log(`Installed Codex skill at ${installSkill()}`);
35
+ console.log(`Installed Claude Code skill at ${installClaudeSkill()}`);
36
+ }
37
+ catch (error) {
38
+ console.warn(`Could not install the codecall skill: ${error instanceof Error ? error.message : String(error)}`);
39
+ }
40
+ }
@@ -0,0 +1,12 @@
1
+ import type { ImplementationContext, LearningOpportunity } from "../schemas/types.js";
2
+ /**
3
+ * Deterministic parity policy for the agent-backed skill. It is deliberately
4
+ * evidence-based and selective; it is not the production authority on a
5
+ * conversation's technical meaning.
6
+ */
7
+ export declare function evaluateLearningOpportunity(context: ImplementationContext): LearningOpportunity;
8
+ /** Deduplicates concept clusters for the lifetime of one runtime/session only. */
9
+ export declare class SessionOpportunityDeduper {
10
+ private readonly evaluatedFingerprints;
11
+ evaluate(context: ImplementationContext): LearningOpportunity;
12
+ }
@@ -0,0 +1,126 @@
1
+ const strongRules = [
2
+ { id: "external_contract_or_integration", match: /\b(oauth|webhook|sdk|integration|third[- ]party|api endpoint|public api|protocol|schema|migration|openapi|graphql)\b/i },
3
+ { id: "architecture_or_cross_component_flow", match: /\b(middleware|pipeline|cross[- ]?(service|component)|event[- ]?driven|controller|service layer|handler|boundary)\b/i },
4
+ { id: "security_or_reliability_boundary", match: /\b(auth(?:entication|orization)?|jwt|token|permission|security|encrypt|concurrenc\w*|race condition|retry|backoff|timeout|idempoten\w*|transaction|consisten\w*|failure handling|error handling)\b/i },
5
+ { id: "consequential_tradeoff", match: /\b(trade[- ]?off|design decision|chose|choose|instead of|compatib(?:ility|le)|backward[- ]?compatible)\b/i }
6
+ ];
7
+ const moderateRules = [
8
+ { id: "reusable_pattern_or_lifecycle", match: /\b(adapter|strategy|factory|observer|cache|queue|algorithm|state machine|lifecycle|framework)\b/i },
9
+ { id: "operational_behavior", match: /\b(config(?:uration)?|environment variable|feature flag|log(?:ging)?|metric|trace|observability|validat(?:e|ion)|test harness|fixture|mock|e2e|ci|deploy(?:ment)?)\b/i }
10
+ ];
11
+ const excludedRules = [
12
+ { id: "formatting_or_copy", match: /\b(format(?:ting)?|prettier|lint(?:ing)?|whitespace|copy change|comment(?:s)?|typo)\b/i },
13
+ { id: "documentation_only", match: /\b(readme|documentation|docs?|changelog)\b/i },
14
+ { id: "mechanical_rename", match: /\b(rename|renaming|move(?:d)? files?)\b/i },
15
+ { id: "dependency_bump", match: /\b(dependency|package) (?:bump|update|upgrade)\b/i },
16
+ { id: "straightforward_local_fix", match: /\b(simple|straightforward|localized|local) (?:bug )?fix\b/i }
17
+ ];
18
+ function implementationText(context) {
19
+ const { implementation } = context;
20
+ return `${implementation.task} ${implementation.conversationSummary ?? ""} ${implementation.changedFiles.map((file) => `${file.path} ${file.summary} ${file.excerpt ?? ""}`).join(" ")}`;
21
+ }
22
+ function isGenerated(file) {
23
+ return /(^|\/)(dist|build|coverage|generated)(\/|$)|\.(lock|snap)$/i.test(file.path) || /generated file/i.test(file.summary);
24
+ }
25
+ function isTestFile(file) {
26
+ return /(^|\/)(test|tests|__tests__|spec)(\/|$)|\.(test|spec)\.[cm]?[jt]sx?$/i.test(file.path);
27
+ }
28
+ function isDocumentationFile(file) {
29
+ return /(^|\/)(readme|changelog|docs?)(\/|\.|$)|\.(md|mdx|txt)$/i.test(file.path);
30
+ }
31
+ function isDependencyFile(file) {
32
+ return /(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|composer\.lock|poetry\.lock)$/i.test(file.path);
33
+ }
34
+ function uniqueMatches(text, rules) {
35
+ return rules.filter((rule) => rule.match.test(text)).map((rule) => rule.id);
36
+ }
37
+ function sourceLabel(context) {
38
+ const file = context.implementation.changedFiles.find((candidate) => !isGenerated(candidate));
39
+ if (file)
40
+ return file.path;
41
+ const evidence = context.evidence.find((candidate) => candidate.path || candidate.label);
42
+ return evidence?.path ?? evidence?.label ?? "the completed implementation";
43
+ }
44
+ /**
45
+ * Deterministic parity policy for the agent-backed skill. It is deliberately
46
+ * evidence-based and selective; it is not the production authority on a
47
+ * conversation's technical meaning.
48
+ */
49
+ export function evaluateLearningOpportunity(context) {
50
+ const { implementation } = context;
51
+ const text = implementationText(context);
52
+ const excludedChangeCategories = uniqueMatches(text, excludedRules);
53
+ if (implementation.changedFiles.some(isGenerated))
54
+ excludedChangeCategories.push("generated_or_lockfile_change");
55
+ const inspectable = Boolean(implementation.task.trim()) && (context.evidence.length > 0 || implementation.changedFiles.length > 0);
56
+ const strongSignals = uniqueMatches(text, strongRules);
57
+ const moderateSignals = uniqueMatches(text, moderateRules);
58
+ const changedSourceFiles = implementation.changedFiles.filter((file) => !isGenerated(file) && !isTestFile(file) && !isDocumentationFile(file) && !isDependencyFile(file));
59
+ const testFiles = implementation.changedFiles.filter(isTestFile);
60
+ const testStrategy = moderateSignals.includes("operational_behavior");
61
+ if (changedSourceFiles.length >= 2 && /\b(import|depend(?:s|ency|ent)|consumer|producer|route|service|handler)\b/i.test(text)) {
62
+ moderateSignals.push("cross_component_dependency");
63
+ }
64
+ if (testFiles.length > 0 && changedSourceFiles.length === 0 && !testStrategy)
65
+ excludedChangeCategories.push("test_only_without_strategy");
66
+ const uniqueStrong = [...new Set(strongSignals)];
67
+ const uniqueModerate = [...new Set(moderateSignals)];
68
+ const meaningfulEvidence = changedSourceFiles.length > 0 || (testFiles.length > 0 && testStrategy);
69
+ const onlyExcluded = implementation.changedFiles.length > 0 && !meaningfulEvidence;
70
+ const conceptFingerprint = `learning-policy/v1:${[...uniqueStrong, ...uniqueModerate].sort().join("+") || "no-meaningful-cluster"}`;
71
+ if (!inspectable || onlyExcluded) {
72
+ const reason = !inspectable
73
+ ? "Skipped automatic learning because the completed implementation lacks inspectable task or file evidence."
74
+ : `Skipped automatic learning because the change is limited to ${[...new Set(excludedChangeCategories)].join(", ")}.`;
75
+ return {
76
+ score: 0,
77
+ confidence: 0.9,
78
+ estimatedMinutes: 0,
79
+ recommendation: "skip",
80
+ signals: {},
81
+ strongSignals: uniqueStrong,
82
+ moderateSignals: uniqueModerate,
83
+ excludedChangeCategories: [...new Set(excludedChangeCategories)],
84
+ conceptFingerprint,
85
+ reasoning: [reason, "Manual /codecall or $codecall remains available."]
86
+ };
87
+ }
88
+ const recommendation = uniqueStrong.length >= 1 || uniqueModerate.length >= 2
89
+ ? "recommend"
90
+ : uniqueModerate.length === 1 ? "optional" : "skip";
91
+ const score = Number(Math.min(1, uniqueStrong.length * 0.65 + uniqueModerate.length * 0.3).toFixed(2));
92
+ const signalNames = [...uniqueStrong, ...uniqueModerate];
93
+ const reason = signalNames.length
94
+ ? `Implementation evidence in ${sourceLabel(context)} indicates ${signalNames.join(", ")}.`
95
+ : `No strong signal or pair of moderate signals was found in ${sourceLabel(context)}.`;
96
+ return {
97
+ score,
98
+ confidence: 0.8,
99
+ estimatedMinutes: recommendation === "recommend" ? Math.min(8, 3 + signalNames.length) : recommendation === "optional" ? 3 : 0,
100
+ recommendation,
101
+ signals: Object.fromEntries([...uniqueStrong.map((id) => [id, 1]), ...uniqueModerate.map((id) => [id, 0.5])]),
102
+ strongSignals: uniqueStrong,
103
+ moderateSignals: uniqueModerate,
104
+ excludedChangeCategories: [...new Set(excludedChangeCategories)],
105
+ conceptFingerprint,
106
+ reasoning: [reason, "Decision is based on implementation evidence and policy signals, never line count."]
107
+ };
108
+ }
109
+ /** Deduplicates concept clusters for the lifetime of one runtime/session only. */
110
+ export class SessionOpportunityDeduper {
111
+ evaluatedFingerprints = new Set();
112
+ evaluate(context) {
113
+ const opportunity = evaluateLearningOpportunity(context);
114
+ if (this.evaluatedFingerprints.has(opportunity.conceptFingerprint)) {
115
+ return {
116
+ ...opportunity,
117
+ recommendation: "skip",
118
+ estimatedMinutes: 0,
119
+ excludedChangeCategories: [...opportunity.excludedChangeCategories, "already_evaluated_in_session"],
120
+ reasoning: [`Skipped automatic learning because ${opportunity.conceptFingerprint} was already evaluated in this session.`, "Manual /codecall or $codecall remains available."]
121
+ };
122
+ }
123
+ this.evaluatedFingerprints.add(opportunity.conceptFingerprint);
124
+ return opportunity;
125
+ }
126
+ }
@@ -0,0 +1,12 @@
1
+ import type { ImplementationLocator } from "./schemas/types.js";
2
+ import { LearnRuntime } from "./runtime/orchestrator.js";
3
+ /** Creates the provider-neutral MVP capability. Providers render its emitted events however they choose. */
4
+ /**
5
+ * Local demonstration runtime. The production experience is the agent-backed
6
+ * Codex skill, which uses the active coding agent's reasoning directly.
7
+ */
8
+ export declare function codecall(implementation: ImplementationLocator): LearnRuntime;
9
+ export * from "./schemas/types.js";
10
+ export * from "./policy/opportunity.js";
11
+ export * from "./runtime/orchestrator.js";
12
+ export * from "./runtime/event-store.js";
package/dist/public.js ADDED
@@ -0,0 +1,24 @@
1
+ import { MemoryEventStore } from "./runtime/event-store.js";
2
+ import { LearnRuntime, contextFromImplementation } from "./runtime/orchestrator.js";
3
+ import { DefaultContextCollector, DependencyLearningPlanner, DeterministicEvaluationEngine, GroundedTeachingEngine, HeuristicConceptExtractor, HeuristicOpportunityDetector, NoopDocumentationResolver, ReasoningQuizEngine } from "./workers/default-workers.js";
4
+ /** Creates the provider-neutral MVP capability. Providers render its emitted events however they choose. */
5
+ /**
6
+ * Local demonstration runtime. The production experience is the agent-backed
7
+ * Codex skill, which uses the active coding agent's reasoning directly.
8
+ */
9
+ export function codecall(implementation) {
10
+ return new LearnRuntime({
11
+ contextCollector: new DefaultContextCollector(contextFromImplementation(implementation)),
12
+ opportunityDetector: new HeuristicOpportunityDetector(),
13
+ conceptExtractor: new HeuristicConceptExtractor(),
14
+ learningPlanner: new DependencyLearningPlanner(),
15
+ documentationResolver: new NoopDocumentationResolver(),
16
+ teachingEngine: new GroundedTeachingEngine(),
17
+ quizEngine: new ReasoningQuizEngine(),
18
+ evaluationEngine: new DeterministicEvaluationEngine()
19
+ }, new MemoryEventStore());
20
+ }
21
+ export * from "./schemas/types.js";
22
+ export * from "./policy/opportunity.js";
23
+ export * from "./runtime/orchestrator.js";
24
+ export * from "./runtime/event-store.js";
@@ -0,0 +1,20 @@
1
+ import type { LearnEvent } from "../schemas/types.js";
2
+ export interface EventStore {
3
+ append(event: LearnEvent): void;
4
+ read(sessionId: string): LearnEvent[];
5
+ }
6
+ export declare class MemoryEventStore implements EventStore {
7
+ private readonly events;
8
+ append(event: LearnEvent): void;
9
+ read(sessionId: string): LearnEvent[];
10
+ }
11
+ /**
12
+ * A deliberately small caller-owned persistence option. The runtime never
13
+ * chooses a storage location or sends events elsewhere; the adapter supplies it.
14
+ */
15
+ export declare class JsonlEventStore implements EventStore {
16
+ private readonly filePath;
17
+ constructor(filePath: string);
18
+ append(event: LearnEvent): void;
19
+ read(sessionId: string): LearnEvent[];
20
+ }
@@ -0,0 +1,32 @@
1
+ import { appendFileSync, existsSync, readFileSync } from "node:fs";
2
+ export class MemoryEventStore {
3
+ events = [];
4
+ append(event) {
5
+ this.events.push(structuredClone(event));
6
+ }
7
+ read(sessionId) {
8
+ return this.events.filter((event) => event.sessionId === sessionId).map((event) => structuredClone(event));
9
+ }
10
+ }
11
+ /**
12
+ * A deliberately small caller-owned persistence option. The runtime never
13
+ * chooses a storage location or sends events elsewhere; the adapter supplies it.
14
+ */
15
+ export class JsonlEventStore {
16
+ filePath;
17
+ constructor(filePath) {
18
+ this.filePath = filePath;
19
+ }
20
+ append(event) {
21
+ appendFileSync(this.filePath, `${JSON.stringify(event)}\n`, "utf8");
22
+ }
23
+ read(sessionId) {
24
+ if (!existsSync(this.filePath))
25
+ return [];
26
+ return readFileSync(this.filePath, "utf8")
27
+ .split("\n")
28
+ .filter(Boolean)
29
+ .map((line) => JSON.parse(line))
30
+ .filter((event) => event.sessionId === sessionId);
31
+ }
32
+ }
@@ -0,0 +1,28 @@
1
+ import type { ConfidenceLevel, ImplementationContext, ImplementationLocator, LearnEvent, Session } from "../schemas/types.js";
2
+ import type { EventStore } from "./event-store.js";
3
+ import type { ConceptExtractor, ContextCollector, DocumentationResolver, EvaluationEngine, LearningPlanner, OpportunityDetector, QuizEngine, TeachingEngine } from "../workers/contracts.js";
4
+ export interface RuntimeWorkers {
5
+ contextCollector: ContextCollector;
6
+ opportunityDetector: OpportunityDetector;
7
+ conceptExtractor: ConceptExtractor;
8
+ learningPlanner: LearningPlanner;
9
+ documentationResolver: DocumentationResolver;
10
+ teachingEngine: TeachingEngine;
11
+ quizEngine: QuizEngine;
12
+ evaluationEngine: EvaluationEngine;
13
+ }
14
+ export declare class LearnRuntime {
15
+ private readonly workers;
16
+ private readonly events;
17
+ constructor(workers: RuntimeWorkers, events: EventStore);
18
+ start(implementation: ImplementationLocator): Promise<Session>;
19
+ decide(session: Session, accepted: boolean): Session;
20
+ setConfidence(session: Session, confidence: ConfidenceLevel): Promise<Session>;
21
+ answer(session: Session, choiceId: string): Promise<Session>;
22
+ cancel(session: Session, reason?: string): Session;
23
+ history(sessionId: string): LearnEvent[];
24
+ private presentCurrentLearningStep;
25
+ private summarize;
26
+ private emit;
27
+ }
28
+ export declare function contextFromImplementation(implementation: ImplementationLocator): ImplementationContext;