@codio-ai/opencode-authoring-agent 0.1.4 → 0.1.6
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/dist/agents/assessment-author.d.ts.map +1 -1
- package/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +356 -106
- package/dist/tools/create-assessment.d.ts +35 -2
- package/dist/tools/create-assessment.d.ts.map +1 -1
- package/dist/tools/fetch-course.d.ts +1 -1
- package/dist/tools/fetch-course.d.ts.map +1 -1
- package/dist/tools/index.d.ts +8 -3
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/publish-course.d.ts +1 -0
- package/dist/tools/publish-course.d.ts.map +1 -1
- package/dist/utils/load-env.d.ts +7 -0
- package/dist/utils/load-env.d.ts.map +1 -0
- package/dist/utils/taskid.d.ts +1 -1
- package/dist/utils/taskid.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/skills/reference-assessment-types/SKILL.md +98 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assessment-author.d.ts","sourceRoot":"","sources":["../../src/agents/assessment-author.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"assessment-author.d.ts","sourceRoot":"","sources":["../../src/agents/assessment-author.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA2C/C,wBAAgB,2BAA2B,CACzC,KAAK,CAAC,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,EACrB,kBAAkB,CAAC,EAAE,MAAM,GAC1B,eAAe,CAkBjB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/agents/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAmD/C,wBAAgB,uBAAuB,CAAC,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/agents/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAmD/C,wBAAgB,uBAAuB,CAAC,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAoG5E;AAED,wBAAgB,4BAA4B,CAC1C,KAAK,CAAC,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,EACrB,kBAAkB,CAAC,EAAE,MAAM,EAC3B,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAC3B,eAAe,CAiBjB"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAUlD,QAAA,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAUlD,QAAA,MAAM,oBAAoB,EA4FT,MAAM,CAAC;AAExB,eAAe,oBAAoB,CAAC;AAEpC,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3945,7 +3945,7 @@ var require_lodash = __commonJS((exports, module) => {
|
|
|
3945
3945
|
}
|
|
3946
3946
|
return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, undefined2, comparator) : [];
|
|
3947
3947
|
});
|
|
3948
|
-
function
|
|
3948
|
+
function join6(array, separator) {
|
|
3949
3949
|
return array == null ? "" : nativeJoin.call(array, separator);
|
|
3950
3950
|
}
|
|
3951
3951
|
function last(array) {
|
|
@@ -5882,7 +5882,7 @@ __p += '`;
|
|
|
5882
5882
|
lodash.isUndefined = isUndefined;
|
|
5883
5883
|
lodash.isWeakMap = isWeakMap;
|
|
5884
5884
|
lodash.isWeakSet = isWeakSet;
|
|
5885
|
-
lodash.join =
|
|
5885
|
+
lodash.join = join6;
|
|
5886
5886
|
lodash.kebabCase = kebabCase;
|
|
5887
5887
|
lodash.last = last;
|
|
5888
5888
|
lodash.lastIndexOf = lastIndexOf;
|
|
@@ -16380,11 +16380,11 @@ var require_path_arg = __commonJS((exports, module) => {
|
|
|
16380
16380
|
|
|
16381
16381
|
// node_modules/tar/node_modules/mkdirp/lib/find-made.js
|
|
16382
16382
|
var require_find_made = __commonJS((exports, module) => {
|
|
16383
|
-
var { dirname:
|
|
16383
|
+
var { dirname: dirname3 } = __require("path");
|
|
16384
16384
|
var findMade = (opts, parent, path = undefined) => {
|
|
16385
16385
|
if (path === parent)
|
|
16386
16386
|
return Promise.resolve();
|
|
16387
|
-
return opts.statAsync(parent).then((st) => st.isDirectory() ? path : undefined, (er) => er.code === "ENOENT" ? findMade(opts,
|
|
16387
|
+
return opts.statAsync(parent).then((st) => st.isDirectory() ? path : undefined, (er) => er.code === "ENOENT" ? findMade(opts, dirname3(parent), parent) : undefined);
|
|
16388
16388
|
};
|
|
16389
16389
|
var findMadeSync = (opts, parent, path = undefined) => {
|
|
16390
16390
|
if (path === parent)
|
|
@@ -16392,7 +16392,7 @@ var require_find_made = __commonJS((exports, module) => {
|
|
|
16392
16392
|
try {
|
|
16393
16393
|
return opts.statSync(parent).isDirectory() ? path : undefined;
|
|
16394
16394
|
} catch (er) {
|
|
16395
|
-
return er.code === "ENOENT" ? findMadeSync(opts,
|
|
16395
|
+
return er.code === "ENOENT" ? findMadeSync(opts, dirname3(parent), parent) : undefined;
|
|
16396
16396
|
}
|
|
16397
16397
|
};
|
|
16398
16398
|
module.exports = { findMade, findMadeSync };
|
|
@@ -16400,10 +16400,10 @@ var require_find_made = __commonJS((exports, module) => {
|
|
|
16400
16400
|
|
|
16401
16401
|
// node_modules/tar/node_modules/mkdirp/lib/mkdirp-manual.js
|
|
16402
16402
|
var require_mkdirp_manual = __commonJS((exports, module) => {
|
|
16403
|
-
var { dirname:
|
|
16403
|
+
var { dirname: dirname3 } = __require("path");
|
|
16404
16404
|
var mkdirpManual = (path, opts, made) => {
|
|
16405
16405
|
opts.recursive = false;
|
|
16406
|
-
const parent =
|
|
16406
|
+
const parent = dirname3(path);
|
|
16407
16407
|
if (parent === path) {
|
|
16408
16408
|
return opts.mkdirAsync(path, opts).catch((er) => {
|
|
16409
16409
|
if (er.code !== "EISDIR")
|
|
@@ -16426,7 +16426,7 @@ var require_mkdirp_manual = __commonJS((exports, module) => {
|
|
|
16426
16426
|
});
|
|
16427
16427
|
};
|
|
16428
16428
|
var mkdirpManualSync = (path, opts, made) => {
|
|
16429
|
-
const parent =
|
|
16429
|
+
const parent = dirname3(path);
|
|
16430
16430
|
opts.recursive = false;
|
|
16431
16431
|
if (parent === path) {
|
|
16432
16432
|
try {
|
|
@@ -16459,12 +16459,12 @@ var require_mkdirp_manual = __commonJS((exports, module) => {
|
|
|
16459
16459
|
|
|
16460
16460
|
// node_modules/tar/node_modules/mkdirp/lib/mkdirp-native.js
|
|
16461
16461
|
var require_mkdirp_native = __commonJS((exports, module) => {
|
|
16462
|
-
var { dirname:
|
|
16462
|
+
var { dirname: dirname3 } = __require("path");
|
|
16463
16463
|
var { findMade, findMadeSync } = require_find_made();
|
|
16464
16464
|
var { mkdirpManual, mkdirpManualSync } = require_mkdirp_manual();
|
|
16465
16465
|
var mkdirpNative = (path, opts) => {
|
|
16466
16466
|
opts.recursive = true;
|
|
16467
|
-
const parent =
|
|
16467
|
+
const parent = dirname3(path);
|
|
16468
16468
|
if (parent === path)
|
|
16469
16469
|
return opts.mkdirAsync(path, opts);
|
|
16470
16470
|
return findMade(opts, path).then((made) => opts.mkdirAsync(path, opts).then(() => made).catch((er) => {
|
|
@@ -16476,7 +16476,7 @@ var require_mkdirp_native = __commonJS((exports, module) => {
|
|
|
16476
16476
|
};
|
|
16477
16477
|
var mkdirpNativeSync = (path, opts) => {
|
|
16478
16478
|
opts.recursive = true;
|
|
16479
|
-
const parent =
|
|
16479
|
+
const parent = dirname3(path);
|
|
16480
16480
|
if (parent === path)
|
|
16481
16481
|
return opts.mkdirSync(path, opts);
|
|
16482
16482
|
const made = findMadeSync(opts, path);
|
|
@@ -16865,7 +16865,7 @@ var require_path_reservations = __commonJS((exports, module) => {
|
|
|
16865
16865
|
var assert = __require("assert");
|
|
16866
16866
|
var normalize = require_normalize_unicode();
|
|
16867
16867
|
var stripSlashes = require_strip_trailing_slashes();
|
|
16868
|
-
var { join:
|
|
16868
|
+
var { join: join6 } = __require("path");
|
|
16869
16869
|
var platform = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
|
|
16870
16870
|
var isWindows = platform === "win32";
|
|
16871
16871
|
module.exports = () => {
|
|
@@ -16874,7 +16874,7 @@ var require_path_reservations = __commonJS((exports, module) => {
|
|
|
16874
16874
|
const getDirs = (path) => {
|
|
16875
16875
|
const dirs = path.split("/").slice(0, -1).reduce((set, path2) => {
|
|
16876
16876
|
if (set.length) {
|
|
16877
|
-
path2 =
|
|
16877
|
+
path2 = join6(set[set.length - 1], path2);
|
|
16878
16878
|
}
|
|
16879
16879
|
set.push(path2 || "/");
|
|
16880
16880
|
return set;
|
|
@@ -16942,7 +16942,7 @@ var require_path_reservations = __commonJS((exports, module) => {
|
|
|
16942
16942
|
};
|
|
16943
16943
|
const reserve = (paths, fn) => {
|
|
16944
16944
|
paths = isWindows ? ["win32 parallelization disabled"] : paths.map((p) => {
|
|
16945
|
-
return stripSlashes(
|
|
16945
|
+
return stripSlashes(join6(normalize(p))).toLowerCase();
|
|
16946
16946
|
});
|
|
16947
16947
|
const dirs = new Set(paths.map((path) => getDirs(path)).reduce((a, b) => a.concat(b)));
|
|
16948
16948
|
reservations.set(fn, { dirs, paths });
|
|
@@ -18231,7 +18231,7 @@ var require_BufferList = __commonJS((exports, module) => {
|
|
|
18231
18231
|
this.head = this.tail = null;
|
|
18232
18232
|
this.length = 0;
|
|
18233
18233
|
};
|
|
18234
|
-
BufferList.prototype.join = function
|
|
18234
|
+
BufferList.prototype.join = function join6(s) {
|
|
18235
18235
|
if (this.length === 0)
|
|
18236
18236
|
return "";
|
|
18237
18237
|
var p = this.head;
|
|
@@ -20411,7 +20411,7 @@ var require_BufferList2 = __commonJS((exports, module) => {
|
|
|
20411
20411
|
this.head = this.tail = null;
|
|
20412
20412
|
this.length = 0;
|
|
20413
20413
|
};
|
|
20414
|
-
BufferList.prototype.join = function
|
|
20414
|
+
BufferList.prototype.join = function join6(s) {
|
|
20415
20415
|
if (this.length === 0)
|
|
20416
20416
|
return "";
|
|
20417
20417
|
var p = this.head;
|
|
@@ -22536,7 +22536,7 @@ var require_buffer_list = __commonJS((exports, module) => {
|
|
|
22536
22536
|
}
|
|
22537
22537
|
}, {
|
|
22538
22538
|
key: "join",
|
|
22539
|
-
value: function
|
|
22539
|
+
value: function join6(s) {
|
|
22540
22540
|
if (this.length === 0)
|
|
22541
22541
|
return "";
|
|
22542
22542
|
var p = this.head;
|
|
@@ -45788,13 +45788,13 @@ ${customAppendPrompt}`;
|
|
|
45788
45788
|
// src/agents/assessment-author.ts
|
|
45789
45789
|
var BASE_PROMPT = `# Assessment Author
|
|
45790
45790
|
|
|
45791
|
-
You author one or more assessments (MCQ, code test, fill-in-the-blanks, parsons-puzzle,
|
|
45791
|
+
You author one or more assessments (MCQ, code test, fill-in-the-blanks, parsons-puzzle, llm-based-auto-rubric, or probeable-question-act) for a Codio guide page. You return structured payloads. **You do not write JSON or files directly.** The orchestrator passes your payload to the \`create_assessment\` tool, which stamps the canonical JSON and writes it.
|
|
45792
45792
|
|
|
45793
45793
|
## Inputs
|
|
45794
45794
|
|
|
45795
|
-
- \`assessment_type\`: one of \`multiple-choice\`, \`code-output-compare\`, \`fill-in-the-blanks\`, \`parsons-puzzle\`, \`llm-based-auto-rubric\`.
|
|
45795
|
+
- \`assessment_type\`: one of \`multiple-choice\`, \`code-output-compare\`, \`fill-in-the-blanks\`, \`parsons-puzzle\`, \`llm-based-auto-rubric\`, \`probeable-question-act\`.
|
|
45796
45796
|
- \`topic\`, \`lo\` (learning objective), \`page_context\` (the page body for grounding).
|
|
45797
|
-
- \`count\` (how many to author; default 1 for code-test/FITB/parsons/auto-rubric, 3 for MCQ).
|
|
45797
|
+
- \`count\` (how many to author; default 1 for code-test/FITB/parsons/auto-rubric/probeable, 3 for MCQ).
|
|
45798
45798
|
|
|
45799
45799
|
## Output
|
|
45800
45800
|
|
|
@@ -45807,6 +45807,16 @@ Return one payload per assessment, in the exact shape \`create_assessment\` expe
|
|
|
45807
45807
|
- For \`fill-in-the-blanks\`: use the \`<<<answer>>>\` marker syntax inline in the \`text\` field. Each blank should test one concrete piece of recall or application.
|
|
45808
45808
|
- For \`parsons-puzzle\`: provide a correct code solution as \`initial\`, then append distractor lines below — each distractor labeled with a \`#distractor\` comment so the orchestrator can review. Aim for 2–4 distractors that reflect common syntactic or logical mistakes (off-by-one, wrong operator, wrong variable name). Indent matters for Python — call out which indent levels each block needs.
|
|
45809
45809
|
- For \`llm-based-auto-rubric\`: write 3–5 rubric items, each scoped to one observable dimension (syntax, logic, edge cases, style). Each item has a clear \`title\` and a \`description\` written for the LLM grader to apply (concrete criteria, not vague). Always include a \`solutions\` array under \`.guides/secure/\` so the grader has reference; never expose solution paths under \`.guides/content/\`.
|
|
45810
|
+
- For \`probeable-question-act\`: collect these fields before returning the payload:
|
|
45811
|
+
- \`functionName\` — Python function name (snake_case), used as the file stem.
|
|
45812
|
+
- \`functionSignature\` — full def line with type hints, e.g. \`def min_index(int_list: list[int]) -> int:\`
|
|
45813
|
+
- \`problemStatement\` — narrative description of what the function must do.
|
|
45814
|
+
- \`inputVariable\` — the variable students modify to probe, e.g. \`int_list\`.
|
|
45815
|
+
- \`inputVariableAnnotation\` — Python type annotation, e.g. \`list[int]\`.
|
|
45816
|
+
- \`defaultInputValue\` — default value shown in the student file, e.g. \`[1, 2, 3]\`.
|
|
45817
|
+
- \`referenceSolution\` — the complete working function definition (including the \`def\` line and body).
|
|
45818
|
+
- \`unitTests\` — full Python unittest file content.
|
|
45819
|
+
**If the instructor has not provided \`referenceSolution\` or \`unitTests\`, ask whether they want to supply them or have you generate them. Generate only if the instructor explicitly says to.**
|
|
45810
45820
|
|
|
45811
45821
|
## What you don't do
|
|
45812
45822
|
|
|
@@ -45819,7 +45829,7 @@ function createAssessmentAuthorAgent(model, customPrompt, customAppendPrompt) {
|
|
|
45819
45829
|
const prompt = resolvePrompt(BASE_PROMPT, customPrompt, customAppendPrompt);
|
|
45820
45830
|
return {
|
|
45821
45831
|
name: "assessment-author",
|
|
45822
|
-
description: "Authors MCQ / code-output-compare / fill-in-the-blanks / parsons-puzzle / llm-based-auto-rubric payloads. Returns structured payloads; does not write files.",
|
|
45832
|
+
description: "Authors MCQ / code-output-compare / fill-in-the-blanks / parsons-puzzle / llm-based-auto-rubric / probeable-question-act payloads. Returns structured payloads; does not write files.",
|
|
45823
45833
|
config: {
|
|
45824
45834
|
mode: "subagent",
|
|
45825
45835
|
model,
|
|
@@ -45854,11 +45864,11 @@ var AGENT_DESCRIPTIONS = {
|
|
|
45854
45864
|
- **Rule of thumb:** New prose or non-trivial edit → @page-author. Trivial fix → yourself.
|
|
45855
45865
|
- **layoutConfig:** When delegating, include \`layoutConfig: { openFiles: string[] }\` derived from the page's \`files\` list so @page-author can generate adaptive file instructions.`,
|
|
45856
45866
|
"assessment-author": `@assessment-author
|
|
45857
|
-
- Role: Authors MCQ / code-output-compare / fill-in-the-blanks / parsons-puzzle / llm-based-auto-rubric payloads. Returns structured payloads only.
|
|
45867
|
+
- Role: Authors MCQ / code-output-compare / fill-in-the-blanks / parsons-puzzle / llm-based-auto-rubric / probeable-question-act payloads. Returns structured payloads only.
|
|
45858
45868
|
- Permissions: Read only
|
|
45859
45869
|
- Stats: Pedagogy + distractor quality is the hardest content judgment in this system. Sonnet-class.
|
|
45860
|
-
- Capabilities: MCQ pedagogy, code-test design, FITB construction, Parsons distractor design, LLM rubric authoring.
|
|
45861
|
-
- **Delegate when:** Authoring any new assessment (multiple-choice, code-output-compare, fill-in-the-blanks, parsons-puzzle, llm-based-auto-rubric).
|
|
45870
|
+
- Capabilities: MCQ pedagogy, code-test design, FITB construction, Parsons distractor design, LLM rubric authoring, probeable question design.
|
|
45871
|
+
- **Delegate when:** Authoring any new assessment (multiple-choice, code-output-compare, fill-in-the-blanks, parsons-puzzle, llm-based-auto-rubric, probeable-question-act).
|
|
45862
45872
|
- **Don't delegate when:** Re-running create_assessment with the same payload (no LLM work needed).
|
|
45863
45873
|
- **Rule of thumb:** New question content → @assessment-author. Re-stamp existing payload → tool only.`,
|
|
45864
45874
|
"source-ingester": `@source-ingester
|
|
@@ -45909,12 +45919,12 @@ ${enabledAgents}
|
|
|
45909
45919
|
|
|
45910
45920
|
### Authoring tools
|
|
45911
45921
|
- \`create_page({workspace, stem, title, type, layout, learningObjectives, files?, markdownBody, chapterFolder?})\` — stamp a page JSON with a fresh v4 UUID and write the JSON+MD pair. Use after \`@page-author\` returns a body+metadata in draft mode. Validates freeze directive syntax before writing — will throw if FREEZE CODE BEGIN/END are unmatched or use wrong comment prefix for the language.
|
|
45912
|
-
- \`create_assessment({type, workspace, payload})\` — stamp an assessment JSON with a fresh taskId and write to \`.guides/assessments/\`. Returns \`{ taskId, jsonPath, embedLine }
|
|
45922
|
+
- \`create_assessment({type, workspace, payload})\` — stamp an assessment JSON with a fresh taskId and write to \`.guides/assessments/\`. Returns \`{ taskId, jsonPath, embedLine }\` for standard types. For \`probeable-question-act\` also returns \`{ studentFilePath, probeScriptPath, probeButtonCommand, runButtonCommand, suggestedPageMarkdown }\` and writes 3 additional files (student file, probe script, unit test file). Use \`suggestedPageMarkdown\` as the body_brief for \`@page-author\` when creating the guide page for a probeable question.
|
|
45913
45923
|
- \`validate_guide({workspace})\` — deterministic structural validation (UUIDs, order arrays, embed references, taskId/filename consistency). Run automatically at the end of new-assignment, import-source, and reorder workflows. Run manually on user request (\`/validate-guide\`).
|
|
45914
45924
|
|
|
45915
45925
|
### Codio platform tools
|
|
45916
|
-
- \`fetch_course({
|
|
45917
|
-
- \`publish_course({manifestPath, dryRun?, force?, module?, assignment?, changelog?, stack?})\` — push course assignments back to the Codio platform using \`course-manifest.json\` as the source of truth. Creates missing courses/modules/assignments automatically. Skips unchanged assignments by content hash. Returns \`{ published, created, unchanged, skipped }\`. **Delegate when:** user wants to publish, push, deploy, or sync assignments to Codio. Always suggest \`dryRun: true\` first to preview.
|
|
45926
|
+
- \`fetch_course({courseId, workspace?, outputDir?, domain?})\` — pull an existing Codio course from the platform into an AI-ready local project. Requires \`CODIO_CLIENT_ID\` and \`CODIO_CLIENT_SECRET\` env vars. Returns \`{ outputDir, manifestPath, moduleCount, assignmentCount }\`. Writes \`course-manifest.json\`, \`course-assignments.csv\`, and extracted assignment content under \`course-content/\`. **Delegate when:** user wants to fetch, pull, import, or export an existing Codio course.
|
|
45927
|
+
- \`publish_course({manifestPath, workspace?, dryRun?, force?, module?, assignment?, changelog?, stack?})\` — push course assignments back to the Codio platform using \`course-manifest.json\` as the source of truth. Creates missing courses/modules/assignments automatically. Skips unchanged assignments by content hash. Returns \`{ published, created, unchanged, skipped }\`. **Delegate when:** user wants to publish, push, deploy, or sync assignments to Codio. Always suggest \`dryRun: true\` first to preview.
|
|
45918
45928
|
|
|
45919
45929
|
### Platform tool routing
|
|
45920
45930
|
- User says "fetch/pull/import/export this course" → \`fetch_course\`
|
|
@@ -45942,7 +45952,8 @@ Only fan out where work is independent — page drafting (Phase 4) and assessmen
|
|
|
45942
45952
|
|
|
45943
45953
|
- New page JSON+MD pair → \`@page-author\` (draft mode) returns body+metadata → you call \`create_page\`. **Do not** hand-write the JSON.
|
|
45944
45954
|
- Existing page surgical edit → \`@page-author\` (revise mode) edits the .md directly. No tool call.
|
|
45945
|
-
- New assessment → \`@assessment-author\` returns payload → you call \`create_assessment\` → you splice the returned \`embedLine\` into the page.
|
|
45955
|
+
- New standard assessment → \`@assessment-author\` returns payload → you call \`create_assessment\` → you splice the returned \`embedLine\` into the page.
|
|
45956
|
+
- New probeable question → \`@assessment-author\` returns payload → you call \`create_assessment\` (type: probeable-question-act) → use \`suggestedPageMarkdown\` as body_brief for \`@page-author\` → call \`create_page\`.
|
|
45946
45957
|
- \`order\` array updates → you edit the index.json yourself with the edit tool. Small contextual JSON; no tool needed.
|
|
45947
45958
|
- Stem generation (kebab-slug + 4-hex) → you generate inline. No tool.
|
|
45948
45959
|
</Workflow>
|
|
@@ -46455,14 +46466,20 @@ var ASSESSMENT_TYPES = [
|
|
|
46455
46466
|
"code-output-compare",
|
|
46456
46467
|
"fill-in-the-blanks",
|
|
46457
46468
|
"parsons-puzzle",
|
|
46458
|
-
"llm-based-auto-rubric"
|
|
46469
|
+
"llm-based-auto-rubric",
|
|
46470
|
+
"probeable-question-act"
|
|
46459
46471
|
];
|
|
46460
|
-
var
|
|
46461
|
-
|
|
46472
|
+
var TASKID_PREFIX = {
|
|
46473
|
+
"probeable-question-act": "test"
|
|
46474
|
+
};
|
|
46475
|
+
var ALL_PREFIXES = [...new Set(ASSESSMENT_TYPES.map((t) => TASKID_PREFIX[t] ?? t))];
|
|
46476
|
+
var TASKID_RE = new RegExp(`^(${ALL_PREFIXES.join("|")})-[1-9][0-9]{9}$`);
|
|
46477
|
+
var ANY_TASKID_RE = new RegExp(`(?:${ALL_PREFIXES.join("|")})-[1-9][0-9]{9}`, "g");
|
|
46462
46478
|
function generateTaskId(type) {
|
|
46479
|
+
const prefix = TASKID_PREFIX[type] ?? type;
|
|
46463
46480
|
const first = randomInt(1, 10);
|
|
46464
46481
|
const rest = randomInt(0, 1e9).toString().padStart(9, "0");
|
|
46465
|
-
return `${
|
|
46482
|
+
return `${prefix}-${first}${rest}`;
|
|
46466
46483
|
}
|
|
46467
46484
|
|
|
46468
46485
|
// src/utils/uuid.ts
|
|
@@ -46481,7 +46498,8 @@ var ASSESSMENT_TYPE_TAG = {
|
|
|
46481
46498
|
"code-output-compare": "Standard Code Test",
|
|
46482
46499
|
"fill-in-the-blanks": "Fill in the Blanks",
|
|
46483
46500
|
"parsons-puzzle": "Parsons Puzzle",
|
|
46484
|
-
"llm-based-auto-rubric": null
|
|
46501
|
+
"llm-based-auto-rubric": null,
|
|
46502
|
+
"probeable-question-act": "Advanced Code Test"
|
|
46485
46503
|
};
|
|
46486
46504
|
function ensureAssessmentTag(tags, type) {
|
|
46487
46505
|
const list = tags ? [...tags] : [];
|
|
@@ -46808,6 +46826,170 @@ function buildLlmAutoRubricAssessment(raw, taskId = generateTaskId("llm-based-au
|
|
|
46808
46826
|
}
|
|
46809
46827
|
};
|
|
46810
46828
|
}
|
|
46829
|
+
var probeablePayloadSchema = z2.object({
|
|
46830
|
+
functionName: z2.string().min(1),
|
|
46831
|
+
functionSignature: z2.string().min(1),
|
|
46832
|
+
problemStatement: z2.string().min(1),
|
|
46833
|
+
inputVariable: z2.string().min(1),
|
|
46834
|
+
inputVariableAnnotation: z2.string().min(1),
|
|
46835
|
+
defaultInputValue: z2.string().min(1),
|
|
46836
|
+
referenceSolution: z2.string().min(1),
|
|
46837
|
+
unitTests: z2.string().min(1),
|
|
46838
|
+
name: z2.string().optional(),
|
|
46839
|
+
points: z2.number().int().nonnegative().default(20),
|
|
46840
|
+
maxAttemptsCount: z2.number().int().nonnegative().default(0),
|
|
46841
|
+
timeoutSeconds: z2.number().int().positive().default(40),
|
|
46842
|
+
guidance: z2.string().default(""),
|
|
46843
|
+
lo: z2.string().default(""),
|
|
46844
|
+
blooms: z2.string().default("")
|
|
46845
|
+
});
|
|
46846
|
+
function indentLines(text, spaces) {
|
|
46847
|
+
const pad = " ".repeat(spaces);
|
|
46848
|
+
return text.split(`
|
|
46849
|
+
`).map((line) => line.trim() ? pad + line : line).join(`
|
|
46850
|
+
`);
|
|
46851
|
+
}
|
|
46852
|
+
function buildProbeableAssessment(raw, taskId, workspace) {
|
|
46853
|
+
const p = probeablePayloadSchema.parse(raw);
|
|
46854
|
+
const fn = p.functionName;
|
|
46855
|
+
const studentFilePath = `code/functions/probeable_${fn}.py`;
|
|
46856
|
+
const probeScriptPath = `.guides/probeable_questions/probe_${fn}.py`;
|
|
46857
|
+
const unitTestPath = `.guides/secure/unit_tests/probeable_questions/${fn}_test.py`;
|
|
46858
|
+
const studentFileContent = [
|
|
46859
|
+
`# Probe ${fn}: Modify only the ${p.inputVariable} values to test edge cases`,
|
|
46860
|
+
``,
|
|
46861
|
+
`${p.inputVariable}: ${p.inputVariableAnnotation} = ${p.defaultInputValue}`,
|
|
46862
|
+
``,
|
|
46863
|
+
`${p.functionSignature}`,
|
|
46864
|
+
` # Replace 'pass' with your solution`,
|
|
46865
|
+
` pass`,
|
|
46866
|
+
``,
|
|
46867
|
+
``,
|
|
46868
|
+
``,
|
|
46869
|
+
`# FREEZE CODE BEGIN`,
|
|
46870
|
+
`if __name__=="__main__":`,
|
|
46871
|
+
` print(f"${p.inputVariable} = {${p.inputVariable}} , result = {${fn}(${p.inputVariable})}")`,
|
|
46872
|
+
`# FREEZE CODE END`
|
|
46873
|
+
].join(`
|
|
46874
|
+
`);
|
|
46875
|
+
const indentedSolution = indentLines(p.referenceSolution.trimEnd(), 4);
|
|
46876
|
+
const moduleName = `probeable_${fn}`;
|
|
46877
|
+
const logFile = `${workspace}/code/functions/probeable_${fn}_io_logs.txt`;
|
|
46878
|
+
const probeScriptContent = [
|
|
46879
|
+
`import sys`,
|
|
46880
|
+
`sys.path.append("${workspace}/code/functions/")`,
|
|
46881
|
+
``,
|
|
46882
|
+
`class MissingVariableError(Exception):`,
|
|
46883
|
+
` def __str__(self):`,
|
|
46884
|
+
` return "Error: The required '${p.inputVariable}' variable is missing. Please declare '${p.inputVariable}' as a ${p.inputVariableAnnotation}."`,
|
|
46885
|
+
``,
|
|
46886
|
+
``,
|
|
46887
|
+
`def check_import_and_probe():`,
|
|
46888
|
+
` try:`,
|
|
46889
|
+
` from ${moduleName} import ${p.inputVariable}`,
|
|
46890
|
+
``,
|
|
46891
|
+
`${indentedSolution}`,
|
|
46892
|
+
``,
|
|
46893
|
+
` if __name__ == "__main__":`,
|
|
46894
|
+
` log_file = "${logFile}"`,
|
|
46895
|
+
` result = ${fn}(${p.inputVariable})`,
|
|
46896
|
+
` with open(log_file, "a") as f:`,
|
|
46897
|
+
` f.write(f"${p.inputVariable} = {${p.inputVariable}} , result = {result}\\n")`,
|
|
46898
|
+
` with open(log_file, "r") as f:`,
|
|
46899
|
+
` for line in f.readlines():`,
|
|
46900
|
+
` print(line)`,
|
|
46901
|
+
``,
|
|
46902
|
+
` except ImportError:`,
|
|
46903
|
+
` raise MissingVariableError()`,
|
|
46904
|
+
`try:`,
|
|
46905
|
+
` check_import_and_probe()`,
|
|
46906
|
+
`except MissingVariableError as e:`,
|
|
46907
|
+
` print(e)`,
|
|
46908
|
+
` sys.exit(1)`
|
|
46909
|
+
].join(`
|
|
46910
|
+
`);
|
|
46911
|
+
const codeEnvConfig = JSON.stringify({
|
|
46912
|
+
type: "python",
|
|
46913
|
+
subtype: "unittest",
|
|
46914
|
+
files: [unitTestPath],
|
|
46915
|
+
maxPoints: p.points,
|
|
46916
|
+
timeout: p.timeoutSeconds,
|
|
46917
|
+
executable: "python3",
|
|
46918
|
+
pythonwd: ".guides/secure/unit_tests/probeable_questions",
|
|
46919
|
+
partialPoints: false
|
|
46920
|
+
});
|
|
46921
|
+
const assessmentJson = {
|
|
46922
|
+
type: "test",
|
|
46923
|
+
taskId,
|
|
46924
|
+
source: {
|
|
46925
|
+
name: p.name ?? fn,
|
|
46926
|
+
showName: false,
|
|
46927
|
+
instructions: "**Submit your work for evaluation**",
|
|
46928
|
+
command: "python /usr/share/codio/assessments/assessments.py",
|
|
46929
|
+
codeEnvConfig,
|
|
46930
|
+
pythonPath: join3(workspace, "code", "functions"),
|
|
46931
|
+
timeoutSeconds: p.timeoutSeconds,
|
|
46932
|
+
guidance: p.guidance,
|
|
46933
|
+
showGuidanceAfterResponseOption: { type: "Never" },
|
|
46934
|
+
maxAttemptsCount: p.maxAttemptsCount,
|
|
46935
|
+
points: p.points,
|
|
46936
|
+
arePartialPointsAllowed: false,
|
|
46937
|
+
useMaximumScore: false,
|
|
46938
|
+
metadata: {
|
|
46939
|
+
tags: [{ name: "Assessment Type", value: "Advanced Code Test" }],
|
|
46940
|
+
files: [studentFilePath],
|
|
46941
|
+
opened: [{ type: "file", panelNumber: 0, content: studentFilePath }]
|
|
46942
|
+
},
|
|
46943
|
+
bloomsObjectiveLevel: p.blooms,
|
|
46944
|
+
learningObjectives: p.lo
|
|
46945
|
+
}
|
|
46946
|
+
};
|
|
46947
|
+
const probeButtonCommand = `python3 ${probeScriptPath}`;
|
|
46948
|
+
const runButtonCommand = `python3 ${studentFilePath}`;
|
|
46949
|
+
const embedLine = `{Check It!|assessment}(${taskId})`;
|
|
46950
|
+
const suggestedPageMarkdown = [
|
|
46951
|
+
`${p.problemStatement}`,
|
|
46952
|
+
``,
|
|
46953
|
+
`#### Probing the solution to find edge cases`,
|
|
46954
|
+
``,
|
|
46955
|
+
`You have the ability to 'probe' the solution. Modify the values in \`${p.inputVariable}\` to check the *expected* return value and discover edge cases. You will see the results of all your inputs.`,
|
|
46956
|
+
``,
|
|
46957
|
+
`|||warning`,
|
|
46958
|
+
`## \`${fn}\` code`,
|
|
46959
|
+
``,
|
|
46960
|
+
`Probing the solution will run the code in the editor. If the code in your file has syntax errors, probing will not work.`,
|
|
46961
|
+
``,
|
|
46962
|
+
`Pro-tip: Leave only the \`pass\` keyword in the \`${fn}\` function while probing.`,
|
|
46963
|
+
`|||`,
|
|
46964
|
+
``,
|
|
46965
|
+
`{Probe Solution}(${probeButtonCommand})`,
|
|
46966
|
+
``,
|
|
46967
|
+
``,
|
|
46968
|
+
`## Complete the function`,
|
|
46969
|
+
``,
|
|
46970
|
+
`Now that you have probed the solution, write your code and complete the \`${fn}\` function.`,
|
|
46971
|
+
``,
|
|
46972
|
+
`{Run it !}(${runButtonCommand})`,
|
|
46973
|
+
``,
|
|
46974
|
+
`${embedLine}`
|
|
46975
|
+
].join(`
|
|
46976
|
+
`);
|
|
46977
|
+
return {
|
|
46978
|
+
taskId,
|
|
46979
|
+
jsonPath: "",
|
|
46980
|
+
embedLine,
|
|
46981
|
+
studentFilePath,
|
|
46982
|
+
probeScriptPath,
|
|
46983
|
+
unitTestPath,
|
|
46984
|
+
probeButtonCommand,
|
|
46985
|
+
runButtonCommand,
|
|
46986
|
+
suggestedPageMarkdown,
|
|
46987
|
+
json: assessmentJson,
|
|
46988
|
+
studentFileContent,
|
|
46989
|
+
probeScriptContent,
|
|
46990
|
+
unitTestContent: p.unitTests
|
|
46991
|
+
};
|
|
46992
|
+
}
|
|
46811
46993
|
var createAssessmentInputSchema = z2.discriminatedUnion("type", [
|
|
46812
46994
|
z2.object({
|
|
46813
46995
|
type: z2.literal("multiple-choice"),
|
|
@@ -46833,6 +47015,11 @@ var createAssessmentInputSchema = z2.discriminatedUnion("type", [
|
|
|
46833
47015
|
type: z2.literal("llm-based-auto-rubric"),
|
|
46834
47016
|
workspace: z2.string().min(1),
|
|
46835
47017
|
payload: z2.unknown()
|
|
47018
|
+
}),
|
|
47019
|
+
z2.object({
|
|
47020
|
+
type: z2.literal("probeable-question-act"),
|
|
47021
|
+
workspace: z2.string().min(1),
|
|
47022
|
+
payload: z2.unknown()
|
|
46836
47023
|
})
|
|
46837
47024
|
]);
|
|
46838
47025
|
async function createAssessmentHandler(raw) {
|
|
@@ -46873,6 +47060,28 @@ async function createAssessmentHandler(raw) {
|
|
|
46873
47060
|
name = json.source.name || taskId;
|
|
46874
47061
|
const parsed = llmAutoRubricPayloadSchema.parse(input.payload);
|
|
46875
47062
|
llmSolutions = parsed.solutions;
|
|
47063
|
+
} else if (input.type === "probeable-question-act") {
|
|
47064
|
+
const taskId = generateTaskId("probeable-question-act");
|
|
47065
|
+
const built = buildProbeableAssessment(input.payload, taskId, workspace);
|
|
47066
|
+
const jsonPath2 = join3(assessments, `${taskId}.json`);
|
|
47067
|
+
try {
|
|
47068
|
+
writeFileSync(jsonPath2, `${JSON.stringify(built.json, null, 2)}
|
|
47069
|
+
`, { flag: "wx" });
|
|
47070
|
+
} catch (err) {
|
|
47071
|
+
if (err.code === "EEXIST")
|
|
47072
|
+
throw new Error(`taskId collision at ${jsonPath2}`);
|
|
47073
|
+
throw err;
|
|
47074
|
+
}
|
|
47075
|
+
const studentAbs = join3(workspace, built.studentFilePath);
|
|
47076
|
+
mkdirSync(dirname(studentAbs), { recursive: true });
|
|
47077
|
+
writeFileSync(studentAbs, built.studentFileContent, { flag: "wx" });
|
|
47078
|
+
const probeAbs = join3(workspace, built.probeScriptPath);
|
|
47079
|
+
mkdirSync(dirname(probeAbs), { recursive: true });
|
|
47080
|
+
writeFileSync(probeAbs, built.probeScriptContent, { flag: "wx" });
|
|
47081
|
+
const unitTestAbs = join3(workspace, built.unitTestPath);
|
|
47082
|
+
mkdirSync(dirname(unitTestAbs), { recursive: true });
|
|
47083
|
+
writeFileSync(unitTestAbs, built.unitTestContent, { flag: "wx" });
|
|
47084
|
+
return { ...built, jsonPath: jsonPath2 };
|
|
46876
47085
|
} else {
|
|
46877
47086
|
throw new Error(`unsupported assessment type: ${input.type}`);
|
|
46878
47087
|
}
|
|
@@ -47034,21 +47243,53 @@ ${freezeErrors.join(`
|
|
|
47034
47243
|
// src/tools/fetch-course.ts
|
|
47035
47244
|
import { execFile } from "node:child_process";
|
|
47036
47245
|
import {
|
|
47037
|
-
|
|
47246
|
+
copyFileSync,
|
|
47247
|
+
existsSync as existsSync4,
|
|
47038
47248
|
mkdirSync as mkdirSync2,
|
|
47039
47249
|
readdirSync,
|
|
47040
47250
|
rmSync,
|
|
47041
47251
|
statSync,
|
|
47042
47252
|
unlinkSync as unlinkSync2
|
|
47043
47253
|
} from "node:fs";
|
|
47044
|
-
import {
|
|
47254
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
47045
47255
|
import { tmpdir } from "node:os";
|
|
47046
|
-
import { dirname as
|
|
47256
|
+
import { dirname as dirname3, join as join6 } from "node:path";
|
|
47047
47257
|
import { promisify } from "node:util";
|
|
47048
47258
|
import { z as z4 } from "zod";
|
|
47259
|
+
|
|
47260
|
+
// src/utils/load-env.ts
|
|
47261
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
47262
|
+
import { dirname as dirname2, join as join5 } from "node:path";
|
|
47263
|
+
function loadEnvWalkUp(startDir) {
|
|
47264
|
+
let dir = startDir.replace(/\/+$/, "");
|
|
47265
|
+
while (true) {
|
|
47266
|
+
const candidate = join5(dir, ".env");
|
|
47267
|
+
if (existsSync3(candidate)) {
|
|
47268
|
+
for (const line of readFileSync2(candidate, "utf8").split(`
|
|
47269
|
+
`)) {
|
|
47270
|
+
const t = line.trim();
|
|
47271
|
+
if (!t || t.startsWith("#"))
|
|
47272
|
+
continue;
|
|
47273
|
+
const eq = t.indexOf("=");
|
|
47274
|
+
if (eq < 1)
|
|
47275
|
+
continue;
|
|
47276
|
+
const key = t.slice(0, eq).trim();
|
|
47277
|
+
const val = t.slice(eq + 1).trim().replace(/^(['"])(.*)\1$/, "$2");
|
|
47278
|
+
process.env[key] = val;
|
|
47279
|
+
}
|
|
47280
|
+
return;
|
|
47281
|
+
}
|
|
47282
|
+
const parent = dirname2(dir);
|
|
47283
|
+
if (parent === dir)
|
|
47284
|
+
return;
|
|
47285
|
+
dir = parent;
|
|
47286
|
+
}
|
|
47287
|
+
}
|
|
47288
|
+
|
|
47289
|
+
// src/tools/fetch-course.ts
|
|
47049
47290
|
var execFileAsync = promisify(execFile);
|
|
47050
47291
|
var fetchCourseInputSchema = z4.object({
|
|
47051
|
-
workspace: z4.string().
|
|
47292
|
+
workspace: z4.string().optional().describe("Absolute path to the Codio workspace root (defaults to current working directory)"),
|
|
47052
47293
|
courseId: z4.string().min(1).describe("Codio course ID (32-char hex)"),
|
|
47053
47294
|
outputDir: z4.string().optional().describe("Output folder name inside workspace (default: course-project)"),
|
|
47054
47295
|
domain: z4.string().optional().describe("Codio domain (default: codio.com)")
|
|
@@ -47063,7 +47304,7 @@ async function extractZip(zipPath, destDir) {
|
|
|
47063
47304
|
function findTarZstFiles(dir) {
|
|
47064
47305
|
const results = [];
|
|
47065
47306
|
for (const name of readdirSync(dir)) {
|
|
47066
|
-
const full =
|
|
47307
|
+
const full = join6(dir, name);
|
|
47067
47308
|
if (statSync(full).isDirectory())
|
|
47068
47309
|
results.push(...findTarZstFiles(full));
|
|
47069
47310
|
else if (name.endsWith(".tar.zst"))
|
|
@@ -47080,10 +47321,10 @@ async function extractTarZstArchives(stagingDir) {
|
|
|
47080
47321
|
}
|
|
47081
47322
|
}
|
|
47082
47323
|
function removeExportedReadmes(dir) {
|
|
47083
|
-
if (!
|
|
47324
|
+
if (!existsSync4(dir))
|
|
47084
47325
|
return;
|
|
47085
47326
|
for (const name of readdirSync(dir)) {
|
|
47086
|
-
const full =
|
|
47327
|
+
const full = join6(dir, name);
|
|
47087
47328
|
if (statSync(full).isDirectory())
|
|
47088
47329
|
removeExportedReadmes(full);
|
|
47089
47330
|
else if (name.toLowerCase() === "readme.md")
|
|
@@ -47093,18 +47334,18 @@ function removeExportedReadmes(dir) {
|
|
|
47093
47334
|
function copyDir(src, dst) {
|
|
47094
47335
|
mkdirSync2(dst, { recursive: true });
|
|
47095
47336
|
for (const name of readdirSync(src)) {
|
|
47096
|
-
const srcPath =
|
|
47097
|
-
const dstPath =
|
|
47337
|
+
const srcPath = join6(src, name);
|
|
47338
|
+
const dstPath = join6(dst, name);
|
|
47098
47339
|
if (statSync(srcPath).isDirectory())
|
|
47099
47340
|
copyDir(srcPath, dstPath);
|
|
47100
|
-
else
|
|
47101
|
-
const { copyFileSync } = __require("node:fs");
|
|
47341
|
+
else
|
|
47102
47342
|
copyFileSync(srcPath, dstPath);
|
|
47103
|
-
}
|
|
47104
47343
|
}
|
|
47105
47344
|
}
|
|
47106
47345
|
async function fetchCourseHandler(raw) {
|
|
47107
47346
|
const input = fetchCourseInputSchema.parse(raw);
|
|
47347
|
+
const workspace = input.workspace?.trim() || process.cwd();
|
|
47348
|
+
loadEnvWalkUp(workspace);
|
|
47108
47349
|
const [{ default: codio }, { json2csv }] = await Promise.all([
|
|
47109
47350
|
Promise.resolve().then(() => __toESM(require_lib3(), 1)),
|
|
47110
47351
|
Promise.resolve().then(() => __toESM(require_converter(), 1))
|
|
@@ -47112,14 +47353,14 @@ async function fetchCourseHandler(raw) {
|
|
|
47112
47353
|
const clientId = process.env["CODIO_CLIENT_ID"];
|
|
47113
47354
|
const clientSecret = process.env["CODIO_CLIENT_SECRET"];
|
|
47114
47355
|
if (!clientId || !clientSecret) {
|
|
47115
|
-
throw new Error("CODIO_CLIENT_ID and CODIO_CLIENT_SECRET must be set as environment variables
|
|
47356
|
+
throw new Error("CODIO_CLIENT_ID and CODIO_CLIENT_SECRET must be set in .env at the workspace root or as environment variables.");
|
|
47116
47357
|
}
|
|
47117
47358
|
const domain = input.domain ?? process.env["CODIO_DOMAIN"] ?? "codio.com";
|
|
47118
47359
|
const outputFolderName = input.outputDir ?? "course-project";
|
|
47119
|
-
const outputDir =
|
|
47120
|
-
const contentDir =
|
|
47121
|
-
const manifestPath =
|
|
47122
|
-
const csvPath =
|
|
47360
|
+
const outputDir = join6(workspace, outputFolderName);
|
|
47361
|
+
const contentDir = join6(outputDir, "course-content");
|
|
47362
|
+
const manifestPath = join6(outputDir, "course-manifest.json");
|
|
47363
|
+
const csvPath = join6(outputDir, "course-assignments.csv");
|
|
47123
47364
|
codio.v1.setDomain(domain);
|
|
47124
47365
|
await codio.v1.auth(clientId, clientSecret);
|
|
47125
47366
|
const courseInfo = await codio.v1.course.info(input.courseId);
|
|
@@ -47158,7 +47399,7 @@ async function fetchCourseHandler(raw) {
|
|
|
47158
47399
|
};
|
|
47159
47400
|
rmSync(outputDir, { recursive: true, force: true });
|
|
47160
47401
|
mkdirSync2(contentDir, { recursive: true });
|
|
47161
|
-
await mkdir(
|
|
47402
|
+
await mkdir(dirname3(manifestPath), { recursive: true });
|
|
47162
47403
|
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
47163
47404
|
`, "utf8");
|
|
47164
47405
|
const rows = modules.flatMap((m) => m.assignments.map((a) => ({
|
|
@@ -47170,14 +47411,14 @@ async function fetchCourseHandler(raw) {
|
|
|
47170
47411
|
assignmentFolder: a.folder,
|
|
47171
47412
|
assignmentPath: a.path
|
|
47172
47413
|
})));
|
|
47173
|
-
const csv =
|
|
47414
|
+
const csv = json2csv(rows, { emptyFieldValue: "", prependHeader: true });
|
|
47174
47415
|
await writeFile(csvPath, csv, "utf8");
|
|
47175
|
-
const workDir =
|
|
47176
|
-
const zipPath =
|
|
47416
|
+
const workDir = join6(tmpdir(), `codio-fetch-${Date.now()}`);
|
|
47417
|
+
const zipPath = join6(workDir, "course-export.zip");
|
|
47177
47418
|
mkdirSync2(workDir, { recursive: true });
|
|
47178
47419
|
try {
|
|
47179
47420
|
await codio.v1.course.downloadSourceExport(input.courseId, zipPath);
|
|
47180
|
-
const stagingDir =
|
|
47421
|
+
const stagingDir = join6(workDir, "staging");
|
|
47181
47422
|
await extractZip(zipPath, stagingDir);
|
|
47182
47423
|
await extractTarZstArchives(stagingDir);
|
|
47183
47424
|
removeExportedReadmes(stagingDir);
|
|
@@ -47197,15 +47438,16 @@ async function fetchCourseHandler(raw) {
|
|
|
47197
47438
|
// src/tools/publish-course.ts
|
|
47198
47439
|
import { createHash } from "node:crypto";
|
|
47199
47440
|
import {
|
|
47200
|
-
existsSync as
|
|
47441
|
+
existsSync as existsSync5,
|
|
47201
47442
|
readdirSync as readdirSync2,
|
|
47202
|
-
readFileSync as
|
|
47203
|
-
writeFileSync as
|
|
47443
|
+
readFileSync as readFileSync3,
|
|
47444
|
+
writeFileSync as writeFileSync3
|
|
47204
47445
|
} from "node:fs";
|
|
47205
|
-
import { join as
|
|
47446
|
+
import { join as join7, relative } from "node:path";
|
|
47206
47447
|
import { z as z5 } from "zod";
|
|
47207
47448
|
var DEFAULT_STACK = "e0195698-d647-4490-8834-350583b532eb:latest";
|
|
47208
47449
|
var publishCourseInputSchema = z5.object({
|
|
47450
|
+
workspace: z5.string().optional().describe("Absolute path to the Codio workspace root where .env lives (defaults to current working directory)"),
|
|
47209
47451
|
manifestPath: z5.string().min(1).describe("Absolute path to course-manifest.json"),
|
|
47210
47452
|
dryRun: z5.boolean().optional().describe("Preview actions without making changes"),
|
|
47211
47453
|
force: z5.boolean().optional().describe("Publish all, bypassing change detection"),
|
|
@@ -47215,17 +47457,17 @@ var publishCourseInputSchema = z5.object({
|
|
|
47215
47457
|
stack: z5.string().optional().describe("Force a specific stack ID for all assignments")
|
|
47216
47458
|
});
|
|
47217
47459
|
function saveManifest(manifestPath, manifest) {
|
|
47218
|
-
|
|
47460
|
+
writeFileSync3(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
47219
47461
|
`, "utf8");
|
|
47220
47462
|
}
|
|
47221
47463
|
function findZip(dir) {
|
|
47222
47464
|
const zip = readdirSync2(dir).find((f) => f.endsWith(".zip"));
|
|
47223
|
-
return zip ?
|
|
47465
|
+
return zip ? join7(dir, zip) : null;
|
|
47224
47466
|
}
|
|
47225
47467
|
function walkFiles(dir) {
|
|
47226
47468
|
const results = [];
|
|
47227
47469
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
47228
|
-
const full =
|
|
47470
|
+
const full = join7(dir, entry.name);
|
|
47229
47471
|
if (entry.isDirectory())
|
|
47230
47472
|
results.push(...walkFiles(full));
|
|
47231
47473
|
else
|
|
@@ -47237,12 +47479,12 @@ function computeContentHash(dir) {
|
|
|
47237
47479
|
const hash = createHash("sha256");
|
|
47238
47480
|
const zipPath = findZip(dir);
|
|
47239
47481
|
if (zipPath) {
|
|
47240
|
-
hash.update(
|
|
47482
|
+
hash.update(readFileSync3(zipPath));
|
|
47241
47483
|
} else {
|
|
47242
47484
|
for (const file of walkFiles(dir).sort()) {
|
|
47243
|
-
hash.update(
|
|
47485
|
+
hash.update(relative(dir, file));
|
|
47244
47486
|
hash.update("\x00");
|
|
47245
|
-
hash.update(
|
|
47487
|
+
hash.update(readFileSync3(file));
|
|
47246
47488
|
hash.update("\x00");
|
|
47247
47489
|
}
|
|
47248
47490
|
}
|
|
@@ -47250,20 +47492,22 @@ function computeContentHash(dir) {
|
|
|
47250
47492
|
}
|
|
47251
47493
|
async function publishCourseHandler(raw) {
|
|
47252
47494
|
const input = publishCourseInputSchema.parse(raw);
|
|
47495
|
+
const workspace = input.workspace?.trim() || process.cwd();
|
|
47496
|
+
loadEnvWalkUp(workspace);
|
|
47253
47497
|
const { default: codio } = await Promise.resolve().then(() => __toESM(require_lib3(), 1));
|
|
47254
47498
|
const clientId = process.env["CODIO_CLIENT_ID"];
|
|
47255
47499
|
const clientSecret = process.env["CODIO_CLIENT_SECRET"];
|
|
47256
47500
|
if (!clientId || !clientSecret) {
|
|
47257
47501
|
throw new Error("CODIO_CLIENT_ID and CODIO_CLIENT_SECRET must be set as environment variables on the box.");
|
|
47258
47502
|
}
|
|
47259
|
-
if (!
|
|
47503
|
+
if (!existsSync5(input.manifestPath)) {
|
|
47260
47504
|
throw new Error(`course-manifest.json not found at ${input.manifestPath}`);
|
|
47261
47505
|
}
|
|
47262
47506
|
const domain = process.env["CODIO_DOMAIN"] ?? "codio.com";
|
|
47263
47507
|
codio.v1.setDomain(domain);
|
|
47264
47508
|
await codio.v1.auth(clientId, clientSecret);
|
|
47265
|
-
const manifest = JSON.parse(
|
|
47266
|
-
const manifestDir =
|
|
47509
|
+
const manifest = JSON.parse(readFileSync3(input.manifestPath, "utf8"));
|
|
47510
|
+
const manifestDir = join7(input.manifestPath, "..");
|
|
47267
47511
|
if (!manifest.courseId) {
|
|
47268
47512
|
const courseName = manifest.courseName ?? "Codio Course";
|
|
47269
47513
|
if (input.dryRun) {
|
|
@@ -47305,8 +47549,8 @@ async function publishCourseHandler(raw) {
|
|
|
47305
47549
|
for (const asgn of mod.assignments) {
|
|
47306
47550
|
if (input.assignment && asgn.name !== input.assignment && asgn.folder !== input.assignment)
|
|
47307
47551
|
continue;
|
|
47308
|
-
const asgnAbsPath =
|
|
47309
|
-
if (!
|
|
47552
|
+
const asgnAbsPath = join7(manifestDir, asgn.path);
|
|
47553
|
+
if (!existsSync5(asgnAbsPath)) {
|
|
47310
47554
|
skipped++;
|
|
47311
47555
|
continue;
|
|
47312
47556
|
}
|
|
@@ -47360,19 +47604,19 @@ async function publishCourseHandler(raw) {
|
|
|
47360
47604
|
}
|
|
47361
47605
|
|
|
47362
47606
|
// src/tools/validate-guide.ts
|
|
47363
|
-
import { existsSync as
|
|
47364
|
-
import { join as
|
|
47607
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
|
|
47608
|
+
import { join as join8, relative as relative2 } from "node:path";
|
|
47365
47609
|
import { z as z6 } from "zod";
|
|
47366
47610
|
var validateGuideInputSchema = z6.object({
|
|
47367
47611
|
workspace: z6.string().min(1)
|
|
47368
47612
|
});
|
|
47369
47613
|
function walkJsonFiles(root) {
|
|
47370
|
-
if (!
|
|
47614
|
+
if (!existsSync6(root))
|
|
47371
47615
|
return [];
|
|
47372
47616
|
const out = [];
|
|
47373
47617
|
for (const name of readdirSync3(root)) {
|
|
47374
|
-
const p =
|
|
47375
|
-
const s =
|
|
47618
|
+
const p = join8(root, name);
|
|
47619
|
+
const s = statSync2(p);
|
|
47376
47620
|
if (s.isDirectory())
|
|
47377
47621
|
out.push(...walkJsonFiles(p));
|
|
47378
47622
|
else if (name.endsWith(".json"))
|
|
@@ -47398,12 +47642,12 @@ var LANG_COMMENT_PREFIX2 = {
|
|
|
47398
47642
|
sql: "--"
|
|
47399
47643
|
};
|
|
47400
47644
|
function walkMdFiles(root) {
|
|
47401
|
-
if (!
|
|
47645
|
+
if (!existsSync6(root))
|
|
47402
47646
|
return [];
|
|
47403
47647
|
const out = [];
|
|
47404
47648
|
for (const name of readdirSync3(root)) {
|
|
47405
|
-
const p =
|
|
47406
|
-
const s =
|
|
47649
|
+
const p = join8(root, name);
|
|
47650
|
+
const s = statSync2(p);
|
|
47407
47651
|
if (s.isDirectory())
|
|
47408
47652
|
out.push(...walkMdFiles(p));
|
|
47409
47653
|
else if (name.endsWith(".md"))
|
|
@@ -47476,7 +47720,7 @@ async function validateGuideHandler(raw) {
|
|
|
47476
47720
|
directory: input.workspace
|
|
47477
47721
|
});
|
|
47478
47722
|
const findings = [];
|
|
47479
|
-
if (!
|
|
47723
|
+
if (!existsSync6(content)) {
|
|
47480
47724
|
findings.push({
|
|
47481
47725
|
category: "missing_content_dir",
|
|
47482
47726
|
detail: `${content} does not exist`,
|
|
@@ -47487,7 +47731,7 @@ async function validateGuideHandler(raw) {
|
|
|
47487
47731
|
for (const f of walkJsonFiles(guides)) {
|
|
47488
47732
|
let parsed;
|
|
47489
47733
|
try {
|
|
47490
|
-
parsed = JSON.parse(
|
|
47734
|
+
parsed = JSON.parse(readFileSync4(f, "utf8"));
|
|
47491
47735
|
} catch {
|
|
47492
47736
|
continue;
|
|
47493
47737
|
}
|
|
@@ -47497,7 +47741,7 @@ async function validateGuideHandler(raw) {
|
|
|
47497
47741
|
if (!isUuidV4(id)) {
|
|
47498
47742
|
findings.push({
|
|
47499
47743
|
category: "invalid_uuid",
|
|
47500
|
-
detail: `${
|
|
47744
|
+
detail: `${relative2(input.workspace, f)}: ${id}`,
|
|
47501
47745
|
severity: "error"
|
|
47502
47746
|
});
|
|
47503
47747
|
}
|
|
@@ -47508,45 +47752,45 @@ async function validateGuideHandler(raw) {
|
|
|
47508
47752
|
continue;
|
|
47509
47753
|
let parsed;
|
|
47510
47754
|
try {
|
|
47511
|
-
parsed = JSON.parse(
|
|
47755
|
+
parsed = JSON.parse(readFileSync4(f, "utf8"));
|
|
47512
47756
|
} catch {
|
|
47513
47757
|
continue;
|
|
47514
47758
|
}
|
|
47515
47759
|
const base = f.slice(0, -"/index.json".length);
|
|
47516
47760
|
for (const stem of parsed?.order ?? []) {
|
|
47517
|
-
const jsonPair =
|
|
47518
|
-
const chapter =
|
|
47761
|
+
const jsonPair = existsSync6(join8(base, `${stem}.json`)) && existsSync6(join8(base, `${stem}.md`));
|
|
47762
|
+
const chapter = existsSync6(join8(base, stem, "index.json"));
|
|
47519
47763
|
if (!jsonPair && !chapter) {
|
|
47520
47764
|
findings.push({
|
|
47521
47765
|
category: "orphan_order_stem",
|
|
47522
|
-
detail: `${
|
|
47766
|
+
detail: `${relative2(input.workspace, f)} references missing stem '${stem}'`,
|
|
47523
47767
|
severity: "error"
|
|
47524
47768
|
});
|
|
47525
47769
|
}
|
|
47526
47770
|
}
|
|
47527
47771
|
}
|
|
47528
47772
|
for (const md of walkMdFiles(content)) {
|
|
47529
|
-
const body =
|
|
47773
|
+
const body = readFileSync4(md, "utf8");
|
|
47530
47774
|
const re = new RegExp(ANY_TASKID_RE.source, "g");
|
|
47531
47775
|
for (const match of body.matchAll(re)) {
|
|
47532
47776
|
const tid = match[0];
|
|
47533
|
-
if (!
|
|
47777
|
+
if (!existsSync6(join8(assessments, `${tid}.json`))) {
|
|
47534
47778
|
findings.push({
|
|
47535
47779
|
category: "broken_assessment_ref",
|
|
47536
|
-
detail: `${
|
|
47780
|
+
detail: `${relative2(input.workspace, md)} references missing ${tid}.json`,
|
|
47537
47781
|
severity: "error"
|
|
47538
47782
|
});
|
|
47539
47783
|
}
|
|
47540
47784
|
}
|
|
47541
47785
|
}
|
|
47542
|
-
if (
|
|
47786
|
+
if (existsSync6(assessments)) {
|
|
47543
47787
|
for (const f of walkJsonFiles(assessments)) {
|
|
47544
47788
|
if (!f.endsWith(".json"))
|
|
47545
47789
|
continue;
|
|
47546
47790
|
const expected = f.split("/").pop().replace(/\.json$/, "");
|
|
47547
47791
|
let parsed;
|
|
47548
47792
|
try {
|
|
47549
|
-
parsed = JSON.parse(
|
|
47793
|
+
parsed = JSON.parse(readFileSync4(f, "utf8"));
|
|
47550
47794
|
} catch {
|
|
47551
47795
|
continue;
|
|
47552
47796
|
}
|
|
@@ -47554,7 +47798,7 @@ async function validateGuideHandler(raw) {
|
|
|
47554
47798
|
if (expected !== actual) {
|
|
47555
47799
|
findings.push({
|
|
47556
47800
|
category: "taskid_filename_mismatch",
|
|
47557
|
-
detail: `${
|
|
47801
|
+
detail: `${relative2(input.workspace, f)}: filename '${expected}' vs taskId '${actual}'`,
|
|
47558
47802
|
severity: "error"
|
|
47559
47803
|
});
|
|
47560
47804
|
}
|
|
@@ -47565,7 +47809,7 @@ async function validateGuideHandler(raw) {
|
|
|
47565
47809
|
continue;
|
|
47566
47810
|
let pageTitle;
|
|
47567
47811
|
try {
|
|
47568
|
-
const parsed = JSON.parse(
|
|
47812
|
+
const parsed = JSON.parse(readFileSync4(f, "utf8"));
|
|
47569
47813
|
pageTitle = parsed?.title;
|
|
47570
47814
|
} catch {
|
|
47571
47815
|
continue;
|
|
@@ -47573,34 +47817,34 @@ async function validateGuideHandler(raw) {
|
|
|
47573
47817
|
if (!pageTitle)
|
|
47574
47818
|
continue;
|
|
47575
47819
|
const mdPath = f.replace(/\.json$/, ".md");
|
|
47576
|
-
if (!
|
|
47820
|
+
if (!existsSync6(mdPath))
|
|
47577
47821
|
continue;
|
|
47578
|
-
const body =
|
|
47822
|
+
const body = readFileSync4(mdPath, "utf8");
|
|
47579
47823
|
const firstH1 = findFirstH1(body);
|
|
47580
47824
|
if (firstH1 && firstH1 === pageTitle) {
|
|
47581
47825
|
findings.push({
|
|
47582
47826
|
category: "double_header",
|
|
47583
|
-
detail: `${
|
|
47827
|
+
detail: `${relative2(input.workspace, mdPath)}: first H1 "${firstH1}" duplicates page title — remove H1 from body`,
|
|
47584
47828
|
severity: "warning"
|
|
47585
47829
|
});
|
|
47586
47830
|
}
|
|
47587
47831
|
}
|
|
47588
47832
|
for (const md of walkMdFiles(content)) {
|
|
47589
|
-
const body =
|
|
47833
|
+
const body = readFileSync4(md, "utf8");
|
|
47590
47834
|
for (const err of checkFreezeDirectives(body)) {
|
|
47591
47835
|
findings.push({
|
|
47592
47836
|
category: "freeze_syntax_error",
|
|
47593
|
-
detail: `${
|
|
47837
|
+
detail: `${relative2(input.workspace, md)}: ${err}`,
|
|
47594
47838
|
severity: "error"
|
|
47595
47839
|
});
|
|
47596
47840
|
}
|
|
47597
47841
|
}
|
|
47598
47842
|
for (const md of walkMdFiles(content)) {
|
|
47599
|
-
const body =
|
|
47843
|
+
const body = readFileSync4(md, "utf8");
|
|
47600
47844
|
for (const err of findMalformedTryIt(body)) {
|
|
47601
47845
|
findings.push({
|
|
47602
47846
|
category: "try_it_syntax_error",
|
|
47603
|
-
detail: `${
|
|
47847
|
+
detail: `${relative2(input.workspace, md)}: ${err}`,
|
|
47604
47848
|
severity: "error"
|
|
47605
47849
|
});
|
|
47606
47850
|
}
|
|
@@ -47610,7 +47854,7 @@ async function validateGuideHandler(raw) {
|
|
|
47610
47854
|
continue;
|
|
47611
47855
|
let parsed;
|
|
47612
47856
|
try {
|
|
47613
|
-
parsed = JSON.parse(
|
|
47857
|
+
parsed = JSON.parse(readFileSync4(f, "utf8"));
|
|
47614
47858
|
} catch {
|
|
47615
47859
|
continue;
|
|
47616
47860
|
}
|
|
@@ -47618,10 +47862,10 @@ async function validateGuideHandler(raw) {
|
|
|
47618
47862
|
const filePath = entry?.path;
|
|
47619
47863
|
if (!filePath)
|
|
47620
47864
|
continue;
|
|
47621
|
-
if (!
|
|
47865
|
+
if (!existsSync6(join8(input.workspace, filePath))) {
|
|
47622
47866
|
findings.push({
|
|
47623
47867
|
category: "missing_referenced_file",
|
|
47624
|
-
detail: `${
|
|
47868
|
+
detail: `${relative2(input.workspace, f)}: references file "${filePath}" which does not exist in workspace`,
|
|
47625
47869
|
severity: "warning"
|
|
47626
47870
|
});
|
|
47627
47871
|
}
|
|
@@ -47640,9 +47884,9 @@ var create_page = tool({
|
|
|
47640
47884
|
}
|
|
47641
47885
|
});
|
|
47642
47886
|
var create_assessment = tool({
|
|
47643
|
-
description: "Create a Codio assessment
|
|
47887
|
+
description: "Create a Codio assessment. Type options: multiple-choice, code-output-compare, fill-in-the-blanks, parsons-puzzle, llm-based-auto-rubric, probeable-question-act. Returns { taskId, jsonPath, embedLine } for standard types. For probeable-question-act also returns { studentFilePath, probeScriptPath, probeButtonCommand, runButtonCommand, suggestedPageMarkdown } and writes student file, probe script, and unit test file.",
|
|
47644
47888
|
args: {
|
|
47645
|
-
type: tool.schema.enum(["multiple-choice", "code-output-compare", "fill-in-the-blanks"]).describe("Assessment type"),
|
|
47889
|
+
type: tool.schema.enum(["multiple-choice", "code-output-compare", "fill-in-the-blanks", "parsons-puzzle", "llm-based-auto-rubric", "probeable-question-act"]).describe("Assessment type"),
|
|
47646
47890
|
workspace: tool.schema.string().min(1).describe("Absolute path to the Codio workspace root"),
|
|
47647
47891
|
payload: tool.schema.unknown().describe("Assessment payload object (type-specific fields)")
|
|
47648
47892
|
},
|
|
@@ -47665,7 +47909,7 @@ var fetch_course = tool({
|
|
|
47665
47909
|
}
|
|
47666
47910
|
});
|
|
47667
47911
|
var publish_course = tool({
|
|
47668
|
-
description: "Publish Codio course assignments to the platform. Reads course-manifest.json, creates missing courses/modules/assignments, and publishes each one. Skips unchanged assignments by content hash. Returns { published, created, unchanged, skipped }.",
|
|
47912
|
+
description: "Publish Codio course assignments to the platform. Reads course-manifest.json from manifestPath, creates missing courses/modules/assignments, and publishes each one. Skips unchanged assignments by content hash. Returns { published, created, unchanged, skipped }.",
|
|
47669
47913
|
args: publishCourseInputSchema.shape,
|
|
47670
47914
|
async execute(args) {
|
|
47671
47915
|
return JSON.stringify(await publishCourseHandler(args));
|
|
@@ -47735,7 +47979,13 @@ var CodioAuthoringPlugin = async (ctx) => {
|
|
|
47735
47979
|
return;
|
|
47736
47980
|
const orchestratorDef = agentDefs.find((a) => a.name === "codio-orchestrator");
|
|
47737
47981
|
const orchestratorPrompt = typeof orchestratorDef?.config?.prompt === "string" ? orchestratorDef.config.prompt : buildOrchestratorPrompt(disabledAgents);
|
|
47738
|
-
|
|
47982
|
+
const workspaceBlock = `<Workspace>
|
|
47983
|
+
Workspace root: ${ctx.directory}
|
|
47984
|
+
Always pass this exact path as the \`workspace\` argument to all tools.
|
|
47985
|
+
</Workspace>`;
|
|
47986
|
+
output.system[0] = orchestratorPrompt + `
|
|
47987
|
+
|
|
47988
|
+
${workspaceBlock}` + (output.system[0] ? `
|
|
47739
47989
|
|
|
47740
47990
|
${output.system[0]}` : "");
|
|
47741
47991
|
}
|
|
@@ -279,6 +279,35 @@ export interface LlmAutoRubricAssessmentJson {
|
|
|
279
279
|
};
|
|
280
280
|
}
|
|
281
281
|
export declare function buildLlmAutoRubricAssessment(raw: unknown, taskId?: string): LlmAutoRubricAssessmentJson;
|
|
282
|
+
export declare const probeablePayloadSchema: z.ZodObject<{
|
|
283
|
+
functionName: z.ZodString;
|
|
284
|
+
functionSignature: z.ZodString;
|
|
285
|
+
problemStatement: z.ZodString;
|
|
286
|
+
inputVariable: z.ZodString;
|
|
287
|
+
inputVariableAnnotation: z.ZodString;
|
|
288
|
+
defaultInputValue: z.ZodString;
|
|
289
|
+
referenceSolution: z.ZodString;
|
|
290
|
+
unitTests: z.ZodString;
|
|
291
|
+
name: z.ZodOptional<z.ZodString>;
|
|
292
|
+
points: z.ZodDefault<z.ZodNumber>;
|
|
293
|
+
maxAttemptsCount: z.ZodDefault<z.ZodNumber>;
|
|
294
|
+
timeoutSeconds: z.ZodDefault<z.ZodNumber>;
|
|
295
|
+
guidance: z.ZodDefault<z.ZodString>;
|
|
296
|
+
lo: z.ZodDefault<z.ZodString>;
|
|
297
|
+
blooms: z.ZodDefault<z.ZodString>;
|
|
298
|
+
}, z.core.$strip>;
|
|
299
|
+
export type ProbeablePayload = z.infer<typeof probeablePayloadSchema>;
|
|
300
|
+
export interface ProbeableCreateResult {
|
|
301
|
+
taskId: string;
|
|
302
|
+
jsonPath: string;
|
|
303
|
+
embedLine: string;
|
|
304
|
+
studentFilePath: string;
|
|
305
|
+
probeScriptPath: string;
|
|
306
|
+
unitTestPath: string;
|
|
307
|
+
probeButtonCommand: string;
|
|
308
|
+
runButtonCommand: string;
|
|
309
|
+
suggestedPageMarkdown: string;
|
|
310
|
+
}
|
|
282
311
|
export declare const createAssessmentInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
283
312
|
type: z.ZodLiteral<"multiple-choice">;
|
|
284
313
|
workspace: z.ZodString;
|
|
@@ -299,12 +328,16 @@ export declare const createAssessmentInputSchema: z.ZodDiscriminatedUnion<[z.Zod
|
|
|
299
328
|
type: z.ZodLiteral<"llm-based-auto-rubric">;
|
|
300
329
|
workspace: z.ZodString;
|
|
301
330
|
payload: z.ZodUnknown;
|
|
331
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
332
|
+
type: z.ZodLiteral<"probeable-question-act">;
|
|
333
|
+
workspace: z.ZodString;
|
|
334
|
+
payload: z.ZodUnknown;
|
|
302
335
|
}, z.core.$strip>], "type">;
|
|
303
|
-
export
|
|
336
|
+
export type CreateAssessmentResult = {
|
|
304
337
|
taskId: string;
|
|
305
338
|
jsonPath: string;
|
|
306
339
|
embedLine: string;
|
|
307
|
-
}
|
|
340
|
+
} | ProbeableCreateResult;
|
|
308
341
|
export declare function createAssessmentHandler(raw: unknown): Promise<CreateAssessmentResult>;
|
|
309
342
|
export {};
|
|
310
343
|
//# sourceMappingURL=create-assessment.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-assessment.d.ts","sourceRoot":"","sources":["../../src/tools/create-assessment.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"create-assessment.d.ts","sourceRoot":"","sources":["../../src/tools/create-assessment.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-course.d.ts","sourceRoot":"","sources":["../../src/tools/fetch-course.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch-course.d.ts","sourceRoot":"","sources":["../../src/tools/fetch-course.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,sBAAsB;;;;;iBAQjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAyDD,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA+GjF"}
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -39,12 +39,15 @@ export declare const create_assessment: {
|
|
|
39
39
|
"multiple-choice": "multiple-choice";
|
|
40
40
|
"code-output-compare": "code-output-compare";
|
|
41
41
|
"fill-in-the-blanks": "fill-in-the-blanks";
|
|
42
|
+
"parsons-puzzle": "parsons-puzzle";
|
|
43
|
+
"llm-based-auto-rubric": "llm-based-auto-rubric";
|
|
44
|
+
"probeable-question-act": "probeable-question-act";
|
|
42
45
|
}>;
|
|
43
46
|
workspace: import("zod").ZodString;
|
|
44
47
|
payload: import("zod").ZodUnknown;
|
|
45
48
|
};
|
|
46
49
|
execute(args: {
|
|
47
|
-
type: "multiple-choice" | "code-output-compare" | "fill-in-the-blanks";
|
|
50
|
+
type: "multiple-choice" | "code-output-compare" | "fill-in-the-blanks" | "parsons-puzzle" | "llm-based-auto-rubric" | "probeable-question-act";
|
|
48
51
|
workspace: string;
|
|
49
52
|
payload: unknown;
|
|
50
53
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
|
|
@@ -61,14 +64,14 @@ export declare const validate_guide: {
|
|
|
61
64
|
export declare const fetch_course: {
|
|
62
65
|
description: string;
|
|
63
66
|
args: {
|
|
64
|
-
workspace: import("zod").ZodString
|
|
67
|
+
workspace: import("zod").ZodOptional<import("zod").ZodString>;
|
|
65
68
|
courseId: import("zod").ZodString;
|
|
66
69
|
outputDir: import("zod").ZodOptional<import("zod").ZodString>;
|
|
67
70
|
domain: import("zod").ZodOptional<import("zod").ZodString>;
|
|
68
71
|
};
|
|
69
72
|
execute(args: {
|
|
70
|
-
workspace: string;
|
|
71
73
|
courseId: string;
|
|
74
|
+
workspace?: string | undefined;
|
|
72
75
|
outputDir?: string | undefined;
|
|
73
76
|
domain?: string | undefined;
|
|
74
77
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<import("@opencode-ai/plugin").ToolResult>;
|
|
@@ -76,6 +79,7 @@ export declare const fetch_course: {
|
|
|
76
79
|
export declare const publish_course: {
|
|
77
80
|
description: string;
|
|
78
81
|
args: {
|
|
82
|
+
workspace: import("zod").ZodOptional<import("zod").ZodString>;
|
|
79
83
|
manifestPath: import("zod").ZodString;
|
|
80
84
|
dryRun: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
81
85
|
force: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
@@ -86,6 +90,7 @@ export declare const publish_course: {
|
|
|
86
90
|
};
|
|
87
91
|
execute(args: {
|
|
88
92
|
manifestPath: string;
|
|
93
|
+
workspace?: string | undefined;
|
|
89
94
|
dryRun?: boolean | undefined;
|
|
90
95
|
force?: boolean | undefined;
|
|
91
96
|
module?: string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAOtB,CAAC;AAOH,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAOtB,CAAC;AAOH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;CAkB5B,CAAC;AAIH,eAAO,MAAM,cAAc;;;;;;;;CAOzB,CAAC;AAIH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;CAOvB,CAAC;AAIH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;CAOzB,CAAC;AAQH,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAMzC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish-course.d.ts","sourceRoot":"","sources":["../../src/tools/publish-course.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"publish-course.d.ts","sourceRoot":"","sources":["../../src/tools/publish-course.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,wBAAwB;;;;;;;;;iBASnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AA6DD,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA4HrF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk up from startDir looking for a .env file.
|
|
3
|
+
* When found, load it with override semantics — .env values always win
|
|
4
|
+
* over whatever is already in process.env.
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadEnvWalkUp(startDir: string): void;
|
|
7
|
+
//# sourceMappingURL=load-env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-env.d.ts","sourceRoot":"","sources":["../../src/utils/load-env.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAoBpD"}
|
package/dist/utils/taskid.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type AssessmentType = 'multiple-choice' | 'code-output-compare' | 'fill-in-the-blanks' | 'parsons-puzzle' | 'llm-based-auto-rubric';
|
|
1
|
+
export type AssessmentType = 'multiple-choice' | 'code-output-compare' | 'fill-in-the-blanks' | 'parsons-puzzle' | 'llm-based-auto-rubric' | 'probeable-question-act';
|
|
2
2
|
export declare const ASSESSMENT_TYPES: AssessmentType[];
|
|
3
3
|
export declare const ANY_TASKID_RE: RegExp;
|
|
4
4
|
export declare function generateTaskId(type: AssessmentType): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"taskid.d.ts","sourceRoot":"","sources":["../../src/utils/taskid.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GACtB,iBAAiB,GACjB,qBAAqB,GACrB,oBAAoB,GACpB,gBAAgB,GAChB,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"taskid.d.ts","sourceRoot":"","sources":["../../src/utils/taskid.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GACtB,iBAAiB,GACjB,qBAAqB,GACrB,oBAAoB,GACpB,gBAAgB,GAChB,uBAAuB,GACvB,wBAAwB,CAAC;AAE7B,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAO5C,CAAC;AAmBF,eAAO,MAAM,aAAa,QAGzB,CAAC;AAEF,wBAAgB,cAAc,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAK3D;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAOpE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: reference-assessment-types
|
|
3
|
-
description: Payload field reference for multiple-choice, code-output-compare
|
|
3
|
+
description: Payload field reference for multiple-choice, code-output-compare, fill-in-the-blanks, parsons-puzzle, llm-based-auto-rubric, and probeable-question-act assessments. Use when building any assessment payload.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Assessment types
|
|
@@ -307,3 +307,100 @@ Note: auto-rubric uses `panelNumber` (not `panel`) in the opened entries.
|
|
|
307
307
|
- All solutions: `.guides/secure/<filename>` — hidden from students.
|
|
308
308
|
- Never: `.guides/content/<anything>` — student-visible.
|
|
309
309
|
- The tool writes the file only if it does not already exist (safe to re-run).
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## `probeable-question-act` — Probeable Question (Advanced Code Test)
|
|
314
|
+
|
|
315
|
+
A probeable question combines a guide page with custom buttons and an Advanced Code Test assessment. Students first "probe" the reference solution with their own inputs to discover edge cases, then implement the function themselves.
|
|
316
|
+
|
|
317
|
+
### What the tool creates
|
|
318
|
+
|
|
319
|
+
Calling `create_assessment` with `type: 'probeable-question-act'` writes four files and returns an extended result:
|
|
320
|
+
|
|
321
|
+
| File | Location | Visible to students? |
|
|
322
|
+
|---|---|---|
|
|
323
|
+
| Assessment JSON | `.guides/assessments/test-XXXXXXXXXX.json` | No |
|
|
324
|
+
| Student file | `code/functions/probeable_{functionName}.py` | Yes |
|
|
325
|
+
| Probe script | `.guides/probeable_questions/probe_{functionName}.py` | Yes (by design) |
|
|
326
|
+
| Unit test file | `.guides/secure/unit_tests/probeable_questions/{functionName}_test.py` | No |
|
|
327
|
+
|
|
328
|
+
The probe script is intentionally student-visible — students click a button to run it with their probe inputs.
|
|
329
|
+
|
|
330
|
+
### Payload fields
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
functionName string Python function name (snake_case), used as the file stem
|
|
334
|
+
functionSignature string Full def line with type hints, e.g. "def min_index(int_list: list[int]) -> int:"
|
|
335
|
+
problemStatement string Narrative description for the guide page
|
|
336
|
+
inputVariable string Variable students modify to probe, e.g. "int_list"
|
|
337
|
+
inputVariableAnnotation string Python type annotation, e.g. "list[int]"
|
|
338
|
+
defaultInputValue string Default value shown in student file, e.g. "[1, 2, 3]"
|
|
339
|
+
referenceSolution string Complete working function definition (def line + body)
|
|
340
|
+
unitTests string Full Python unittest file content
|
|
341
|
+
name? string Display name (defaults to functionName)
|
|
342
|
+
points? number Default 20
|
|
343
|
+
maxAttemptsCount? number Default 0 (unlimited)
|
|
344
|
+
timeoutSeconds? number Default 40
|
|
345
|
+
guidance? string Default ""
|
|
346
|
+
lo? string Learning objective
|
|
347
|
+
blooms? string Bloom's level
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Extended return value
|
|
351
|
+
|
|
352
|
+
```
|
|
353
|
+
taskId string e.g. "test-1234567890"
|
|
354
|
+
jsonPath string Absolute path to written assessment JSON
|
|
355
|
+
embedLine string '{Check It!|assessment}(test-XXXXXXXXXX)'
|
|
356
|
+
studentFilePath string 'code/functions/probeable_{functionName}.py'
|
|
357
|
+
probeScriptPath string '.guides/probeable_questions/probe_{functionName}.py'
|
|
358
|
+
unitTestPath string '.guides/secure/unit_tests/probeable_questions/{functionName}_test.py'
|
|
359
|
+
probeButtonCommand string 'python3 .guides/probeable_questions/probe_{functionName}.py'
|
|
360
|
+
runButtonCommand string 'python3 code/functions/probeable_{functionName}.py'
|
|
361
|
+
suggestedPageMarkdown string Complete guide page markdown — pass to @page-author as body_brief
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Orchestrator workflow
|
|
365
|
+
|
|
366
|
+
```
|
|
367
|
+
@assessment-author → payload
|
|
368
|
+
↓
|
|
369
|
+
create_assessment(type: probeable-question-act) → { taskId, embedLine, suggestedPageMarkdown, ... }
|
|
370
|
+
↓
|
|
371
|
+
@page-author (draft mode, body_brief = suggestedPageMarkdown) → markdownBody
|
|
372
|
+
↓
|
|
373
|
+
create_page (layout: 2-panels-tree, files: [studentFilePath]) → page JSON + MD
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Guide page structure
|
|
377
|
+
|
|
378
|
+
The `suggestedPageMarkdown` returned by the tool follows this template — pass it as-is or refine via `@page-author`:
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
{problemStatement}
|
|
382
|
+
|
|
383
|
+
#### Probing the solution to find edge cases
|
|
384
|
+
|
|
385
|
+
Modify {inputVariable} values to check the expected return value and discover edge cases.
|
|
386
|
+
|
|
387
|
+
|||warning
|
|
388
|
+
## `{functionName}` code
|
|
389
|
+
Probing will run the code in the editor. Syntax errors in your file will prevent probing.
|
|
390
|
+
Pro-tip: Leave only `pass` in the `{functionName}` function while probing.
|
|
391
|
+
|||
|
|
392
|
+
|
|
393
|
+
{Probe Solution}(python3 .guides/probeable_questions/probe_{functionName}.py)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
## Complete the function
|
|
397
|
+
|
|
398
|
+
{Run it !}(python3 code/functions/probeable_{functionName}.py)
|
|
399
|
+
|
|
400
|
+
{Check It!|assessment}(test-XXXXXXXXXX)
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### File naming
|
|
404
|
+
|
|
405
|
+
Files are named by `functionName` (no sequential exercise numbers). If a student or probe file with the same `functionName` already exists, the tool throws a collision error — choose a unique function name per question.
|
|
406
|
+
|