diff --git a/CHANGELOG.md b/CHANGELOG.md index 402ca1b..57c2359 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to `sustech-cli` are documented in this file. ## [Unreleased] +## [0.12.1] - 2026-09-12 + +### Fixed + +- Maintenance fixes. + ## [0.12.0] - 2026-09-12 ### Added diff --git a/README.md b/README.md index fcf7ed7..7fae810 100644 --- a/README.md +++ b/README.md @@ -259,7 +259,7 @@ review. A successful envelope looks like this: "ok": true, "command": "version", "data": { - "version": "0.12.0", + "version": "0.12.1", "runtime": "node v22.19.0" } } diff --git a/docs/TIS_COURSE_DETAIL.md b/docs/TIS_COURSE_DETAIL.md index 0af0e69..6b9159a 100644 --- a/docs/TIS_COURSE_DETAIL.md +++ b/docs/TIS_COURSE_DETAIL.md @@ -43,9 +43,10 @@ HTML is converted to plain text; embedded scripts/styles are removed. - Total, undergraduate, graduate, male, female, internal (`对内`) and external (`对外`) counts are separate marginals. Their presence does not establish a breakdown by department, major or year, or a cross-tabulation of those groups. -- Zero remains zero. Missing fields remain absent. Percentages are emitted only - when both component counts reconcile with a positive total. Quotas and counts - paired with quotas are separate from aggregate enrollment. +- Zero remains zero. Missing fields remain absent. The undergraduate + percentage is emitted only when undergraduate and graduate counts reconcile + with a positive total. Quotas and counts paired with quotas are separate from + aggregate enrollment. - A missing prerequisite list does not establish that a course has no requirements. The upstream description preserves alternatives such as “CS112 or CS109”; listed course references are not interpreted as an AND rule. diff --git a/package-lock.json b/package-lock.json index 986f80d..1646d33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sustech-cli", - "version": "0.12.0", + "version": "0.12.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sustech-cli", - "version": "0.12.0", + "version": "0.12.1", "license": "PolyForm-Noncommercial-1.0.0", "dependencies": { "@modelcontextprotocol/server": "^2.0.0", diff --git a/package.json b/package.json index 914aef6..e078e1c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sustech-cli", - "version": "0.12.0", + "version": "0.12.1", "description": "Human-friendly and agent-ready command-line access to SUSTech services.", "license": "PolyForm-Noncommercial-1.0.0", "type": "module", diff --git a/src/core/version.ts b/src/core/version.ts index 58e9e3f..8c7fbe5 100644 --- a/src/core/version.ts +++ b/src/core/version.ts @@ -1,2 +1,2 @@ -export const CLI_VERSION = "0.12.0"; +export const CLI_VERSION = "0.12.1"; export const USER_AGENT = `sustech-cli/${CLI_VERSION} (+https://github.com/wormforce/sustech-cli)`; diff --git a/src/test/cli.test.ts b/src/test/cli.test.ts index 0ddbac7..32c92bd 100644 --- a/src/test/cli.test.ts +++ b/src/test/cli.test.ts @@ -31,7 +31,7 @@ test("compiled CLI serves human text and versioned JSON from the real entrypoint const text = run(["version"]); assert.equal(text.status, 0); assert.match(text.stdout, /:\*##: :#######:/); - assert.match(text.stdout, /sustech-cli 0\.12\.0/); + assert.match(text.stdout, /sustech-cli 0\.12\.1/); assert.doesNotMatch(text.stdout, /\u001b\[/); const json = run(["version", "--json"]); @@ -40,7 +40,7 @@ test("compiled CLI serves human text and versioned JSON from the real entrypoint schemaVersion: "1", ok: true, command: "version", - data: { version: "0.12.0", runtime: `node ${process.version}` }, + data: { version: "0.12.1", runtime: `node ${process.version}` }, }); }); diff --git a/src/test/tis-course-detail.test.ts b/src/test/tis-course-detail.test.ts index 8bab059..5c59f37 100644 --- a/src/test/tis-course-detail.test.ts +++ b/src/test/tis-course-detail.test.ts @@ -54,7 +54,7 @@ test("course detail preserves zero counts, separate quotas and prerequisite alte assert.equal(result.enrollment.counts.male, 0); assert.equal(result.enrollment.counts.graduate, 0); assert.equal(result.enrollment.counts.internal, undefined); - assert.equal(result.enrollment.femalePercentage, 100); + assert.equal(result.enrollment.counts.female, 10); assert.equal(result.enrollment.undergraduatePercentage, 100); assert.equal(result.enrollment.quotaCounts.total, 8); assert.equal(result.enrollment.counts.total, 10); @@ -72,7 +72,7 @@ test("course detail preserves zero counts, separate quotas and prerequisite alte assert.equal(calls.find(call => call.path.endsWith("xsckViewByxk"))?.body.kcid, "COURSE-LIBRARY-ID"); assert.doesNotMatch(JSON.stringify(result), /PRIVATE_|SECRET_SQL|DO_NOT_/); const rendered = formatCourseDetail(result); - assert.match(rendered, /male 0/); + assert.match(rendered, /male 0 · female 10/); assert.match(rendered, /Week 1\nWeek 2/); assert.match(rendered, /Bring laptop/); assert.doesNotMatch(rendered, /
|PRIVATE_|SECRET_SQL/); @@ -114,7 +114,8 @@ test("round enrichment reuses selection transport and exposes only matching task const result = await new TisClient(session as unknown as TisSession).courseDetail(semester, { code: "DEMO101", round: "bxxk" }); assert.equal(result.enrollment.source, "available"); assert.equal(result.enrollment.counts.total, 12); - assert.equal(result.enrollment.femalePercentage, 33.3); + assert.equal(result.enrollment.counts.male, 8); + assert.equal(result.enrollment.counts.female, 4); assert.equal(result.enrollment.status?.label, "候补"); assert.equal(result.selection?.period, "补退选"); assert.ok(result.notices.some(row => row.kind === "time-conflict")); @@ -140,7 +141,8 @@ test("unavailable selection rounds do not discard course-library details", async test("inconsistent or unknown aggregate counts do not become invented percentages", async () => { const { session } = fixture({ selection: [{ ...task, yxzrs: "10", nansyxrs: "8", nvsyxrs: "5", bksyxrs: "", yjsyxrs: null, sxbj: "W" }] }); const result = await readCourseDetail(session, semester, { code: "DEMO101" }, noSelection); - assert.equal(result.enrollment.femalePercentage, undefined); + assert.equal(result.enrollment.counts.male, 8); + assert.equal(result.enrollment.counts.female, 5); assert.equal(result.enrollment.undergraduatePercentage, undefined); assert.equal(result.enrollment.counts.undergraduate, undefined); assert.deepEqual(result.enrollment.status, { code: "W", label: "未知" }); diff --git a/src/tis/course-detail-text.ts b/src/tis/course-detail-text.ts index d134b6a..d7ab606 100644 --- a/src/tis/course-detail-text.ts +++ b/src/tis/course-detail-text.ts @@ -15,7 +15,6 @@ export function formatCourseDetail(report: TisCourseDetail): string { ` ${counts(enrollment.counts)}`, ` Quotas: ${counts(enrollment.quotas)}`, ` Counts paired with quotas: ${counts(enrollment.quotaCounts)}`); - if (enrollment.femalePercentage !== undefined) lines.push(` Female: ${enrollment.femalePercentage}%`); if (enrollment.undergraduatePercentage !== undefined) lines.push(` Undergraduate: ${enrollment.undergraduatePercentage}%`); if (enrollment.status) lines.push(` My selection status: ${enrollment.status.label} (${enrollment.status.code})`); if (report.selection) { diff --git a/src/tis/course-detail.ts b/src/tis/course-detail.ts index ad5b9e1..680bbb7 100644 --- a/src/tis/course-detail.ts +++ b/src/tis/course-detail.ts @@ -41,7 +41,6 @@ export interface TisCourseDetail { counts: PopulationCounts; quotas: PopulationCounts; quotaCounts: PopulationCounts; - femalePercentage?: number; undergraduatePercentage?: number; status?: { code: string; label: string }; }; @@ -201,7 +200,6 @@ export async function readCourseDetail( enrollment: { source: available ? "available" : enrolled ? "enrolled" : cart ? "cart" : "catalog", counts, quotas, quotaCounts, - ...percentage("femalePercentage", counts.female, counts.male, counts.total), ...percentage("undergraduatePercentage", counts.undergraduate, counts.graduate, counts.total), ...(enrolled && scalar(enrolled.sxbj) ? { status: enrollmentStatus(scalar(enrolled.sxbj)) } : {}), },