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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
Expand Down
7 changes: 4 additions & 3 deletions docs/TIS_COURSE_DETAIL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion src/core/version.ts
Original file line number Diff line number Diff line change
@@ -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)`;
4 changes: 2 additions & 2 deletions src/test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"]);
Expand All @@ -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}` },
});
});

Expand Down
10 changes: 6 additions & 4 deletions src/test/tis-course-detail.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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, /<p>|PRIVATE_|SECRET_SQL/);
Expand Down Expand Up @@ -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"));
Expand All @@ -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: "未知" });
Expand Down
1 change: 0 additions & 1 deletion src/tis/course-detail-text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 0 additions & 2 deletions src/tis/course-detail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export interface TisCourseDetail {
counts: PopulationCounts;
quotas: PopulationCounts;
quotaCounts: PopulationCounts;
femalePercentage?: number;
undergraduatePercentage?: number;
status?: { code: string; label: string };
};
Expand Down Expand Up @@ -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)) } : {}),
},
Expand Down