@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.
@@ -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;AAiC/C,wBAAgB,2BAA2B,CACzC,KAAK,CAAC,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,EACrB,kBAAkB,CAAC,EAAE,MAAM,GAC1B,eAAe,CAkBjB"}
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,CAmG5E;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"}
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"}
@@ -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,EA0FT,MAAM,CAAC;AAExB,eAAe,oBAAoB,CAAC;AAEpC,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC"}
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 join5(array, separator) {
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 = join5;
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: dirname2 } = __require("path");
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, dirname2(parent), parent) : undefined);
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, dirname2(parent), parent) : undefined;
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: dirname2 } = __require("path");
16403
+ var { dirname: dirname3 } = __require("path");
16404
16404
  var mkdirpManual = (path, opts, made) => {
16405
16405
  opts.recursive = false;
16406
- const parent = dirname2(path);
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 = dirname2(path);
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: dirname2 } = __require("path");
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 = dirname2(path);
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 = dirname2(path);
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: join5 } = __require("path");
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 = join5(set[set.length - 1], 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(join5(normalize(p))).toLowerCase();
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 join5(s) {
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 join5(s) {
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 join5(s) {
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, or llm-based-auto-rubric) 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.
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 }\`. Use after \`@assessment-author\` returns a payload. The returned \`embedLine\` is what you splice into the page markdown.
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({workspace, courseId, 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.
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 TASKID_RE = new RegExp(`^(${ASSESSMENT_TYPES.join("|")})-[1-9][0-9]{9}$`);
46461
- var ANY_TASKID_RE = new RegExp(`(?:${ASSESSMENT_TYPES.join("|")})-[1-9][0-9]{9}`, "g");
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 `${type}-${first}${rest}`;
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
- existsSync as existsSync3,
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 { writeFile, mkdir } from "node:fs/promises";
47254
+ import { mkdir, writeFile } from "node:fs/promises";
47045
47255
  import { tmpdir } from "node:os";
47046
- import { dirname as dirname2, join as join5, relative } from "node:path";
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().min(1).describe("Absolute path to the Codio workspace root"),
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 = join5(dir, name);
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 (!existsSync3(dir))
47324
+ if (!existsSync4(dir))
47084
47325
  return;
47085
47326
  for (const name of readdirSync(dir)) {
47086
- const full = join5(dir, name);
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 = join5(src, name);
47097
- const dstPath = join5(dst, name);
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 on the box.");
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 = join5(input.workspace, outputFolderName);
47120
- const contentDir = join5(outputDir, "course-content");
47121
- const manifestPath = join5(outputDir, "course-manifest.json");
47122
- const csvPath = join5(outputDir, "course-assignments.csv");
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(dirname2(manifestPath), { recursive: true });
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 = await json2csv(rows, { emptyFieldValue: "", prependHeader: true });
47414
+ const csv = json2csv(rows, { emptyFieldValue: "", prependHeader: true });
47174
47415
  await writeFile(csvPath, csv, "utf8");
47175
- const workDir = join5(tmpdir(), `codio-fetch-${Date.now()}`);
47176
- const zipPath = join5(workDir, "course-export.zip");
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 = join5(workDir, "staging");
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 existsSync4,
47441
+ existsSync as existsSync5,
47201
47442
  readdirSync as readdirSync2,
47202
- readFileSync as readFileSync2,
47203
- writeFileSync as writeFileSync4
47443
+ readFileSync as readFileSync3,
47444
+ writeFileSync as writeFileSync3
47204
47445
  } from "node:fs";
47205
- import { join as join6, relative as relative2 } from "node:path";
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
- writeFileSync4(manifestPath, `${JSON.stringify(manifest, null, 2)}
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 ? join6(dir, zip) : null;
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 = join6(dir, entry.name);
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(readFileSync2(zipPath));
47482
+ hash.update(readFileSync3(zipPath));
47241
47483
  } else {
47242
47484
  for (const file of walkFiles(dir).sort()) {
47243
- hash.update(relative2(dir, file));
47485
+ hash.update(relative(dir, file));
47244
47486
  hash.update("\x00");
47245
- hash.update(readFileSync2(file));
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 (!existsSync4(input.manifestPath)) {
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(readFileSync2(input.manifestPath, "utf8"));
47266
- const manifestDir = join6(input.manifestPath, "..");
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 = join6(manifestDir, asgn.path);
47309
- if (!existsSync4(asgnAbsPath)) {
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 existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
47364
- import { join as join7, relative as relative3 } from "node:path";
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 (!existsSync5(root))
47614
+ if (!existsSync6(root))
47371
47615
  return [];
47372
47616
  const out = [];
47373
47617
  for (const name of readdirSync3(root)) {
47374
- const p = join7(root, name);
47375
- const s = statSync3(p);
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 (!existsSync5(root))
47645
+ if (!existsSync6(root))
47402
47646
  return [];
47403
47647
  const out = [];
47404
47648
  for (const name of readdirSync3(root)) {
47405
- const p = join7(root, name);
47406
- const s = statSync3(p);
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 (!existsSync5(content)) {
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(readFileSync3(f, "utf8"));
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: `${relative3(input.workspace, f)}: ${id}`,
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(readFileSync3(f, "utf8"));
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 = existsSync5(join7(base, `${stem}.json`)) && existsSync5(join7(base, `${stem}.md`));
47518
- const chapter = existsSync5(join7(base, stem, "index.json"));
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: `${relative3(input.workspace, f)} references missing stem '${stem}'`,
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 = readFileSync3(md, "utf8");
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 (!existsSync5(join7(assessments, `${tid}.json`))) {
47777
+ if (!existsSync6(join8(assessments, `${tid}.json`))) {
47534
47778
  findings.push({
47535
47779
  category: "broken_assessment_ref",
47536
- detail: `${relative3(input.workspace, md)} references missing ${tid}.json`,
47780
+ detail: `${relative2(input.workspace, md)} references missing ${tid}.json`,
47537
47781
  severity: "error"
47538
47782
  });
47539
47783
  }
47540
47784
  }
47541
47785
  }
47542
- if (existsSync5(assessments)) {
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(readFileSync3(f, "utf8"));
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: `${relative3(input.workspace, f)}: filename '${expected}' vs taskId '${actual}'`,
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(readFileSync3(f, "utf8"));
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 (!existsSync5(mdPath))
47820
+ if (!existsSync6(mdPath))
47577
47821
  continue;
47578
- const body = readFileSync3(mdPath, "utf8");
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: `${relative3(input.workspace, mdPath)}: first H1 "${firstH1}" duplicates page title — remove H1 from body`,
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 = readFileSync3(md, "utf8");
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: `${relative3(input.workspace, md)}: ${err}`,
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 = readFileSync3(md, "utf8");
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: `${relative3(input.workspace, md)}: ${err}`,
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(readFileSync3(f, "utf8"));
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 (!existsSync5(join7(input.workspace, filePath))) {
47865
+ if (!existsSync6(join8(input.workspace, filePath))) {
47622
47866
  findings.push({
47623
47867
  category: "missing_referenced_file",
47624
- detail: `${relative3(input.workspace, f)}: references file "${filePath}" which does not exist in workspace`,
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 JSON of type multiple-choice, code-output-compare, or fill-in-the-blanks. Generates the taskId, writes to .guides/assessments/, returns { taskId, jsonPath, embedLine }.",
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
- output.system[0] = orchestratorPrompt + (output.system[0] ? `
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 interface CreateAssessmentResult {
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":"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"}
1
+ {"version":3,"file":"create-assessment.d.ts","sourceRoot":"","sources":["../../src/tools/create-assessment.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  export declare const fetchCourseInputSchema: z.ZodObject<{
3
- workspace: z.ZodString;
3
+ workspace: z.ZodOptional<z.ZodString>;
4
4
  courseId: z.ZodString;
5
5
  outputDir: z.ZodOptional<z.ZodString>;
6
6
  domain: z.ZodOptional<z.ZodString>;
@@ -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;AAIxB,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;AAoED,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0GjF"}
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"}
@@ -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;;;;;;;;;;;;;;;;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
+ {"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,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  export declare const publishCourseInputSchema: z.ZodObject<{
3
+ workspace: z.ZodOptional<z.ZodString>;
3
4
  manifestPath: z.ZodString;
4
5
  dryRun: z.ZodOptional<z.ZodBoolean>;
5
6
  force: z.ZodOptional<z.ZodBoolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"publish-course.d.ts","sourceRoot":"","sources":["../../src/tools/publish-course.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,wBAAwB;;;;;;;;iBAQnC,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,CAuHrF"}
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"}
@@ -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;AAE5B,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAM5C,CAAC;AAMF,eAAO,MAAM,aAAa,QAGzB,CAAC;AAEF,wBAAgB,cAAc,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAI3D;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAKpE"}
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": "@codio-ai/opencode-authoring-agent",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Opencode plugin for authoring Codio guide assignments — orchestrator, subagents, tools, and skills.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: reference-assessment-types
3
- description: Payload field reference for multiple-choice, code-output-compare (Standard Code Test), fill-in-the-blanks, parsons-puzzle, and llm-based-auto-rubric assessments. Use when building any assessment payload.
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
+