icoa-cli 2.19.402 → 2.19.404
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/commands/ai4ctf.js +1 -1
- package/dist/commands/ai4ioai.js +1 -1
- package/dist/commands/arena.js +1 -1
- package/dist/commands/ctf.js +1 -1
- package/dist/commands/ctf4ai-demo.js +1 -1
- package/dist/commands/ctf4vla.js +1 -1
- package/dist/commands/demo2.js +1 -1
- package/dist/commands/exam.js +1 -1
- package/dist/commands/ioailab.js +1 -1
- package/dist/commands/ipynb.js +1 -1
- package/dist/commands/learn.js +1 -1
- package/dist/commands/sim.js +1 -1
- package/dist/lib/kaggle.js +1 -1
- package/dist/lib/learn-ai.d.ts +19 -0
- package/dist/lib/learn-ai.js +1 -1
- package/dist/lib/learn-contest.d.ts +7 -4
- package/dist/lib/learn-render.d.ts +13 -4
- package/dist/lib/learn-render.js +1 -1
- package/package.json +1 -1
package/dist/lib/kaggle.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export function parseKaggleTaskList(e){const t=e??{},r=(t.data??t).tasks;if(!Array.isArray(r))return[];const i=[];for(const e of r){if(!e||"object"!=typeof e)continue;const t=e;"string"==typeof t.comp_id&&""!==t.comp_id&&i.push({comp_id:t.comp_id,title:"string"==typeof t.title?t.title:t.comp_id,metric:"string"==typeof t.metric?t.metric:"accuracy",description:"string"==typeof t.description&&""!==t.description?t.description:void 0,kaggle_url:"string"==typeof t.kaggle_url?t.kaggle_url:null,licence:"string"==typeof t.licence?t.licence:void 0,attribution:"string"==typeof t.attribution?t.attribution:void 0,data_base:"string"==typeof t.data_base&&""!==t.data_base?t.data_base:null,id_col:"string"==typeof t.id_col?t.id_col:void 0,target_col:"string"==typeof t.target_col?t.target_col:void 0,n_train:"number"==typeof t.n_train?t.n_train:void 0,n_test:"number"==typeof t.n_test?t.n_test:void 0,judge:"string"==typeof t.judge&&""!==t.judge?t.judge:void 0,board_event:"string"==typeof t.board_event&&""!==t.board_event?t.board_event:null,needs_gpu:!0===t.needs_gpu})}return i}export function dataFileUrls(e){const t=e.data_base;if("string"!=typeof t||""===t)return null;const r=t.replace(/\/$/,"");return{train:`${r}/train.csv`,test:`${r}/test.csv`,sample_submission:`${r}/sample_submission.csv`}}export function buildStarterCells(e,t){const r=t.id_col??"id",i=t.target_col??"target",a=(o=t.metric,/rmse|mse|mae|error/i.test(o));var o;const s="icoa-holdout"===t.judge,n=e=>({kind:"markdown",source:e,result:null}),l=e=>({kind:"code",source:e.join("\n"),result:null}),c=[`# ${t.title}`,"",...t.description?[t.description,""]:[],`Kaggle-style practice — metric: **${t.metric}**.`,"","- `train.csv` — labelled training data",`- \`test.csv\` — rows to predict (no \`${i}\` column)`,"- `sample_submission.csv` — the exact format Kaggle expects","","Run all cells top to bottom: a baseline model trains on your CPU and","`submission.csv` appears next to the data. Then improve the model cell",...s?["and re-run — `icoa kaggle submit` scores it and ranks you on the board."]:["and re-run — your score is whatever Kaggle says it is."],"","**AI help:** split your terminal (`Ctrl-b %`), run `icoa` in the right pane","and type `ai4ioai` for a live Gemma
|
|
1
|
+
export function parseKaggleTaskList(e){const t=e??{},r=(t.data??t).tasks;if(!Array.isArray(r))return[];const i=[];for(const e of r){if(!e||"object"!=typeof e)continue;const t=e;"string"==typeof t.comp_id&&""!==t.comp_id&&i.push({comp_id:t.comp_id,title:"string"==typeof t.title?t.title:t.comp_id,metric:"string"==typeof t.metric?t.metric:"accuracy",description:"string"==typeof t.description&&""!==t.description?t.description:void 0,kaggle_url:"string"==typeof t.kaggle_url?t.kaggle_url:null,licence:"string"==typeof t.licence?t.licence:void 0,attribution:"string"==typeof t.attribution?t.attribution:void 0,data_base:"string"==typeof t.data_base&&""!==t.data_base?t.data_base:null,id_col:"string"==typeof t.id_col?t.id_col:void 0,target_col:"string"==typeof t.target_col?t.target_col:void 0,n_train:"number"==typeof t.n_train?t.n_train:void 0,n_test:"number"==typeof t.n_test?t.n_test:void 0,judge:"string"==typeof t.judge&&""!==t.judge?t.judge:void 0,board_event:"string"==typeof t.board_event&&""!==t.board_event?t.board_event:null,needs_gpu:!0===t.needs_gpu})}return i}export function dataFileUrls(e){const t=e.data_base;if("string"!=typeof t||""===t)return null;const r=t.replace(/\/$/,"");return{train:`${r}/train.csv`,test:`${r}/test.csv`,sample_submission:`${r}/sample_submission.csv`}}export function buildStarterCells(e,t){const r=t.id_col??"id",i=t.target_col??"target",a=(o=t.metric,/rmse|mse|mae|error/i.test(o));var o;const s="icoa-holdout"===t.judge,n=e=>({kind:"markdown",source:e,result:null}),l=e=>({kind:"code",source:e.join("\n"),result:null}),c=[`# ${t.title}`,"",...t.description?[t.description,""]:[],`Kaggle-style practice — metric: **${t.metric}**.`,"","- `train.csv` — labelled training data",`- \`test.csv\` — rows to predict (no \`${i}\` column)`,"- `sample_submission.csv` — the exact format Kaggle expects","","Run all cells top to bottom: a baseline model trains on your CPU and","`submission.csv` appears next to the data. Then improve the model cell",...s?["and re-run — `icoa kaggle submit` scores it and ranks you on the board."]:["and re-run — your score is whatever Kaggle says it is."],"","**AI help:** split your terminal (`Ctrl-b %`), run `icoa` in the right pane","and type `ai4ioai` for a live Gemma 3n E4B. Ask it for feature ideas or bug fixes —","then verify every suggestion with the val score. A tip that lowers it, drop it.",...t.licence?["",`**Data licence:** ${t.licence}`]:[],...t.attribution?[`**Attribution:** ${t.attribution}`]:[]].join("\n"),d=l(["# setup — load the data (leave as is)","import os","import pandas as pd","from pandas.api.types import is_numeric_dtype","from sklearn.model_selection import train_test_split",a?"from sklearn.metrics import mean_squared_error":"from sklearn.metrics import accuracy_score","","# same code everywhere: in a GPU session these env vars point at the","# box's own dataset copy + workspace — locally they are unset.",`DIR = os.environ.get("ICOA_KAGGLE_DATA") or r"${e}"`,'OUT = os.environ.get("ICOA_KAGGLE_OUT") or DIR',...t.data_base?[`DATA_BASE = "${t.data_base.replace(/\/$/,"")}"`,'if not os.path.exists(DIR + "/train.csv"):'," import urllib.request"," os.makedirs(DIR, exist_ok=True)",' for f in ("train.csv", "test.csv", "sample_submission.csv"):',' urllib.request.urlretrieve(DATA_BASE + "/" + f, DIR + "/" + f)']:[],'train = pd.read_csv(DIR + "/train.csv")','test = pd.read_csv(DIR + "/test.csv")',`ID, TARGET = "${r}", "${i}"`,'print(f"train {train.shape} · test {test.shape}")']),m=l(["# FEATURES — turn raw columns into numbers a model can use (improve THIS cell).","# Numbers: fill gaps with the median. Text/category: one-hot the low-cardinality","# ones (<= 20 distinct); drop high-cardinality text like names/IDs (rarely helps","# a baseline). Train and test end up aligned to the exact same columns.","feat = [c for c in train.columns if c not in (ID, TARGET)]","Xtr_raw, Xte_raw = train[feat].copy(), test[feat].copy()","num = [c for c in feat if is_numeric_dtype(Xtr_raw[c])]","cat = [c for c in feat if c not in num and Xtr_raw[c].nunique() <= 20]","for c in num:"," med = Xtr_raw[c].median()"," Xtr_raw[c] = Xtr_raw[c].fillna(med)"," Xte_raw[c] = Xte_raw[c].fillna(med)","X = pd.get_dummies(Xtr_raw[num + cat], columns=cat, dummy_na=True)","X_test = pd.get_dummies(Xte_raw[num + cat], columns=cat, dummy_na=True)",'X, X_test = X.align(X_test, join="left", axis=1, fill_value=0)',"y = train[TARGET]","X_tr, X_val, y_tr, y_val = train_test_split(X, y, test_size=0.2, random_state=0)",'print(f"{X.shape[1]} model-ready features "',' f"(dropped {len(feat) - len(num) - len(cat)} high-cardinality/text cols)")']),u=l(a?["# MODEL — a working baseline. Improve THIS cell to climb the Kaggle board.","from sklearn.linear_model import Ridge","model = Ridge()"]:["# MODEL — a working baseline. Improve THIS cell to climb the Kaggle board.","from sklearn.linear_model import LogisticRegression","model = LogisticRegression(max_iter=2000)"]),p=l(["# validate — quick local score on a held-out fifth of train","model.fit(X_tr, y_tr)",a?'print("val RMSE:", round(mean_squared_error(y_val, model.predict(X_val)) ** 0.5, 4))':'print("val accuracy:", round(accuracy_score(y_val, model.predict(X_val)), 4))']),g=l(["# submission — retrain on ALL data, write submission.csv (Kaggle format)","model.fit(X, y)","pred = model.predict(X_test)",'out = OUT + "/submission.csv"',`pd.DataFrame({"${r}": test["${r}"], "${i}": pred}).to_csv(out, index=False)`,'print("wrote", out)',s?`print("score it: icoa kaggle submit ${t.comp_id}")`:'print("format check: icoa kaggle check")']),_=s?["## Submit to ICOA (scored on the server)","",`1. In the ICOA CLI run \`icoa kaggle submit ${t.comp_id}\``,"2. The server scores `submission.csv` against the secret test labels"," and your rank lands on the live board (`icoa board`).","3. Improve the MODEL cell, re-run, submit again — best score counts."]:t.kaggle_url?["## Submit on Kaggle (manual, your own account)","",`1. Open ${t.kaggle_url}`,"2. **Submit Predictions** → upload `submission.csv`","3. Read your leaderboard score, come back, improve the MODEL cell, repeat."]:["## Submit on Kaggle (manual, your own account)","","_This is a placeholder practice dataset — it has no live Kaggle page yet._","When the real competition dataset lands, this cell will link straight to","its Kaggle submission page. The loop is identical: upload `submission.csv`,","read the score, improve the MODEL cell, repeat."];return[n(c),d,m,u,p,g,n(_.join("\n"))]}export function parseSubmitResponse(e){const t=e&&"object"==typeof e?e:{},r=t.data??t,i=r&&"object"==typeof r?r:{};return"number"==typeof i.score&&"string"==typeof i.metric?{ok:!0,score:i.score,metric:i.metric,rank:"number"==typeof i.rank_at_time?i.rank_at_time:null,fieldSize:"number"==typeof i.field_size?i.field_size:null,deduped:!0===i.deduped,boardEvent:"string"==typeof i.board_event?i.board_event:null}:"string"==typeof i.reason&&""!==i.reason?{ok:!1,reason:i.reason,error:"string"==typeof i.error?i.error:void 0,problems:Array.isArray(i.problems)?i.problems.filter(e=>"string"==typeof e):void 0}:{ok:!1,reason:"network"}}export function validateSubmissionText(e,t){const r=[],i=e.trim().split(/\r?\n/),a=t.trim().split(/\r?\n/),o=i[0]??"",s=a[0]??"";o.trim()!==s.trim()&&r.push(`header mismatch: expected "${s}", got "${o}"`);const n=i.slice(1).filter(e=>""!==e.trim()),l=a.slice(1).filter(e=>""!==e.trim());n.length!==l.length&&r.push(`row count mismatch: expected ${l.length}, got ${n.length}`);const c=e=>new Set(e.map(e=>(e.split(",")[0]??"").trim())),d=c(n),m=c(l),u=[...m].filter(e=>!d.has(e)),p=[...d].filter(e=>!m.has(e));if(u.length>0||p.length>0){const e=[];u.length>0&&e.push(`${u.length} missing (e.g. ${u[0]})`),p.length>0&&e.push(`${p.length} unknown (e.g. ${p[0]})`),r.push(`id mismatch vs sample_submission: ${e.join(", ")}`)}for(let e=0;e<n.length;e++)if(n[e].split(",").some(e=>""===e.trim())){r.push(`empty cell in data row ${e+1}`);break}return{ok:0===r.length,problems:r}}
|
package/dist/lib/learn-ai.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export type PqsTopEntry = {
|
|
|
45
45
|
score: number;
|
|
46
46
|
tier: string;
|
|
47
47
|
me: boolean;
|
|
48
|
+
name?: string | null;
|
|
48
49
|
questions: string[];
|
|
49
50
|
};
|
|
50
51
|
export type PqsTopResult = {
|
|
@@ -80,6 +81,13 @@ export type HonorBoardEntry = {
|
|
|
80
81
|
tier: string;
|
|
81
82
|
icon: string;
|
|
82
83
|
me: boolean;
|
|
84
|
+
name?: string | null;
|
|
85
|
+
};
|
|
86
|
+
export type HonorMine = {
|
|
87
|
+
best_score: number;
|
|
88
|
+
rank: number;
|
|
89
|
+
entrants: number;
|
|
90
|
+
submissions_left: number;
|
|
83
91
|
};
|
|
84
92
|
export type HonorCardMedal = {
|
|
85
93
|
card: number;
|
|
@@ -98,6 +106,8 @@ export type HonorChapter = {
|
|
|
98
106
|
export type HonorsResult = {
|
|
99
107
|
ok: boolean;
|
|
100
108
|
board?: HonorBoardEntry[];
|
|
109
|
+
mine?: HonorMine | null;
|
|
110
|
+
nickname?: string | null;
|
|
101
111
|
totals?: {
|
|
102
112
|
gold: number;
|
|
103
113
|
silver: number;
|
|
@@ -113,6 +123,15 @@ export declare function viewHonors(opts: {
|
|
|
113
123
|
curriculumId?: string;
|
|
114
124
|
cardNumber?: number;
|
|
115
125
|
}): Promise<HonorsResult>;
|
|
126
|
+
export declare function setNickname(opts: {
|
|
127
|
+
token: string;
|
|
128
|
+
nickname: string;
|
|
129
|
+
}): Promise<{
|
|
130
|
+
ok: boolean;
|
|
131
|
+
nickname?: string;
|
|
132
|
+
code?: string;
|
|
133
|
+
message?: string;
|
|
134
|
+
}>;
|
|
116
135
|
export declare function streamLearnTutor(opts: {
|
|
117
136
|
token: string;
|
|
118
137
|
cardNumber: number;
|
package/dist/lib/learn-ai.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getDeviceFingerprint as e}from"./access.js";import{LEARN_SERVER as t}from"./learn-curricula.js";export async function submitPqs(
|
|
1
|
+
import{getDeviceFingerprint as e}from"./access.js";import{LEARN_SERVER as t}from"./learn-curricula.js";export async function submitPqs(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/submit";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),card_number:a.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(18e4)}),n=await t.json().catch(()=>({}));if(!t.ok||!n.success||!n.data)return{ok:!1,message:n.message||`HTTP ${t.status}`,code:n.code};const o=n.data;return{ok:!0,score:o.score,tier:o.tier,manipulation:o.manipulation,feedback:o.feedback,improve:o.improve,submissionIndex:o.submission_index,submissionsLeft:o.submissions_left,rank:o.rank,entrants:o.entrants,inTop3:o.in_top3,turns:o.turns,spent:o.spent,cap:o.cap}}catch(e){return{ok:!1,message:e instanceof Error&&"TimeoutError"===e.name?"PQS grading timed out — your conversation is safe on the server; try /submit again in a minute.":`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function viewPqsTop(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/view";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),card_number:a.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,top3:n.data.top3,myRank:n.data.my_rank,entrants:n.data.entrants}:{ok:!1,message:n.message||`HTTP ${t.status}`,code:n.code,myRank:n.rank,entrants:n.entrants}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function fetchMyScores(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/myscores";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),card_lo:a.cardLo,card_hi:a.cardHi,device_fingerprint:e()}),signal:AbortSignal.timeout(8e3)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,scores:n.data.scores??[]}:{ok:!1,message:n.message||`HTTP ${t.status}`,code:n.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function viewHonors(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/honors";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),curriculum_id:a.curriculumId,card_number:a.cardNumber,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,board:n.data.board,mine:n.data.mine??null,nickname:n.data.nickname??null,totals:n.data.totals,chapters:n.data.chapters,floor:n.data.floor}:{ok:!1,message:n.message||`HTTP ${t.status}`,code:n.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function setNickname(a){const r=t.replace(/\/$/,"")+"/api/icoa/learn/nickname";try{const t=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),nickname:a.nickname,device_fingerprint:e()}),signal:AbortSignal.timeout(3e4)}),n=await t.json().catch(()=>({}));return t.ok&&n.success&&n.data?{ok:!0,nickname:n.data.nickname}:{ok:!1,message:n.message||`HTTP ${t.status}`,code:n.code}}catch(e){return{ok:!1,message:`Network error: ${e instanceof Error?e.message:String(e)}`}}}export async function streamLearnTutor(a,r){const n=t.replace(/\/$/,"")+"/api/icoa/learn/ai",o=new AbortController;let s=setTimeout(()=>o.abort(),6e4);const i=()=>{clearTimeout(s),s=setTimeout(()=>o.abort(),6e4)};try{const t=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:a.token.toUpperCase(),card_number:a.cardNumber,question:a.question,mode:a.mode,history:a.history?.slice(-6),device_fingerprint:e()}),signal:o.signal});if(!t.ok||!t.body){const e=await t.json().catch(()=>({}));return{ok:!1,answer:"",message:e.message||`HTTP ${t.status}`,code:e.code,spent:e.spent,cap:e.cap}}const s=t.body.getReader(),c=new TextDecoder;let d="",m="",p=null,u=null;for(;;){const e=await s.read();if(e.done)break;i(),d+=c.decode(e.value,{stream:!0});const t=d.split("\n");d=t.pop()??"";for(const e of t){const t=e.trim();if(!t.startsWith("data:"))continue;let a;try{a=JSON.parse(t.slice(5).trim())}catch{continue}a.r&&r({kind:"reasoning",text:a.r}),a.c&&(m+=a.c,r({kind:"content",text:a.c})),a.error&&(u=a.error),a.done&&(p=a)}}return u&&!m?{ok:!1,answer:"",message:u}:m?{ok:!0,answer:m,offtopic:!0===p?.offtopic,spent:p?.spent,cap:p?.cap}:{ok:!1,answer:"",message:"No answer received — try again."}}catch(e){return{ok:!1,answer:"",message:e instanceof Error&&"AbortError"===e.name?"AI tutor timed out (no data for 60s) — try again.":`Network error: ${e instanceof Error?e.message:String(e)}`}}finally{clearTimeout(s)}}
|
|
@@ -53,10 +53,13 @@ export type GatePhase = {
|
|
|
53
53
|
passed: boolean;
|
|
54
54
|
contest?: {
|
|
55
55
|
passed: boolean;
|
|
56
|
-
best_ac
|
|
57
|
-
need
|
|
58
|
-
n
|
|
59
|
-
attempts
|
|
56
|
+
best_ac?: number;
|
|
57
|
+
need?: number;
|
|
58
|
+
n?: number;
|
|
59
|
+
attempts?: number;
|
|
60
|
+
groups_passed?: number;
|
|
61
|
+
group_count?: number;
|
|
62
|
+
slots?: SlotState[];
|
|
60
63
|
};
|
|
61
64
|
research?: {
|
|
62
65
|
good_cards: number;
|
|
@@ -3,6 +3,7 @@ import type { LearnState } from './learn-state.js';
|
|
|
3
3
|
export declare function renderWelcome(curriculum: Curriculum, state: LearnState, isNewSession: boolean): void;
|
|
4
4
|
export declare function setLearnAiEnabled(on: boolean): void;
|
|
5
5
|
export declare function setLearnPqsViewCards(cards: ReadonlySet<number>): void;
|
|
6
|
+
export declare function setLearnHonorRanks(ranks: ReadonlyMap<number, number>): void;
|
|
6
7
|
export type HonorWallData = {
|
|
7
8
|
totals: {
|
|
8
9
|
gold: number;
|
|
@@ -47,10 +48,18 @@ export declare function renderPhaseComplete(curriculum: Curriculum, state: Learn
|
|
|
47
48
|
configured: boolean;
|
|
48
49
|
contest?: {
|
|
49
50
|
passed: boolean;
|
|
50
|
-
best_ac
|
|
51
|
-
need
|
|
52
|
-
n
|
|
53
|
-
attempts
|
|
51
|
+
best_ac?: number;
|
|
52
|
+
need?: number;
|
|
53
|
+
n?: number;
|
|
54
|
+
attempts?: number;
|
|
55
|
+
slots?: Array<{
|
|
56
|
+
slot: number;
|
|
57
|
+
passed: boolean;
|
|
58
|
+
best_ac: number;
|
|
59
|
+
need: number;
|
|
60
|
+
n: number;
|
|
61
|
+
attempts: number;
|
|
62
|
+
}>;
|
|
54
63
|
};
|
|
55
64
|
research?: {
|
|
56
65
|
good_cards: number;
|
package/dist/lib/learn-render.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import chalk from"chalk";import o from"string-width";import{localized as e}from"./learn-curricula.js";import{t as n}from"./learn-i18n.js";import{getConfig as l}from"./config.js";import{icoaBannerLines as t}from"./banner.js";import{renderCharts as r,detectCaps as s}from"./render-card.js";function a(){return(l().language||"en").toLowerCase()}const c=64;function g(o="─"){return o.repeat(66)}function i(o,e,n=20){const l=e>0?o/e:0,t=Math.floor(l*n),r=n-t;return`${chalk.green("█".repeat(t))+chalk.gray("░".repeat(r))} ${(100*l).toFixed(1)}%`}function d(o){return o>=30?chalk.red("🔥🔥🔥 "+o+" days"):o>=7?chalk.yellow("🔥 "+o+" days"):o>=1?chalk.gray("· "+o+" day"+(o>1?"s":"")):chalk.gray("—")}export function renderWelcome(o,e,l){const t=a(),r=e.cardsCompleted.length,s=o.totalCards,c=s-r,y=o.modules.find(o=>e.currentCard>=o.cardRange[0]&&e.currentCard<=o.cardRange[1]),u=/demo/i.test(o.id);if(console.log(),console.log(chalk.cyan(" ╭"+g("═"))),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ║ ")+chalk.bold.white(" "+(o.name?.trim()||n("academy_title",t)))),console.log(chalk.cyan(" ║")),l)console.log(chalk.cyan(" ║ ")+chalk.white(" "+n(u?"welcome_new_demo":"welcome_new",t)));else{const o=new Date(e.lastSeenAt),l=Math.floor((Date.now()-o.getTime())/36e5),r=l<1?n("just_now",t):l<24?`${l}${n("ago_hours",t)}`:`${Math.floor(l/24)}${n("ago_days",t)}`;console.log(chalk.cyan(" ║ ")+chalk.white(` ${n("welcome_back",t)} ${r}`))}console.log(chalk.cyan(" ║"));const h=y?`${y.number}. ${y.name}`:"—";console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("module",t).padEnd(11))+chalk.white(h)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("progress",t).padEnd(11))+i(r,s)+chalk.gray(` (${r}/${s})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("streak",t).padEnd(11))+d(e.streakDays)),c>0&&e.currentCard<=s&&console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("next_card",t).padEnd(11))+chalk.white(`#${e.currentCard} ${n("of",t)} ${s}`)),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ╰"+g("═"))),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const m=0===r?n("continue_start",t):`${n("continue_resume",t)} ${e.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+m)),console.log(chalk.yellow(" status")+chalk.gray(" "+n("status_full",t))),e.bookmarks.length>0&&console.log(chalk.yellow(" bookmarks")+chalk.gray(` ${e.bookmarks.length} ${n("bookmarks_desc",t)}`)),console.log(chalk.gray(" quit")+chalk.gray(" "+n("quit_desc",t))),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log()}function y(o,e){const l=a(),t=o.module,r=e.modules.find(o=>o.number===t),s=r?r.name:n("unknown_module",l),g=n("module",l).replace(":","").trim(),i=`${n("card",l)} ${o.number} / ${e.totalCards} · ${g} ${t} · ${s}`;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(i)+" "+chalk.cyan("─".repeat(Math.max(0,c-i.length-4))))}function u(){console.log(chalk.cyan(" ╰"+g())),console.log()}function h(o){console.log(chalk.cyan(" │ ")+chalk.white(o))}function m(){console.log(chalk.cyan(" │"))}const b=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function f(o){return/^(?: {4}|\t)/.test(o)||b.test(o)||/ {3,}/.test(o)}function p(e,n){const l=[];let t="",r=0,s=!1;for(const a of function(e){const n=[],l=e=>{let l="";const t=()=>{l&&(n.push({t:"atom",s:l}),l="")};for(const r of e)" "===r?(t(),n.push({t:"space"})):2===o(r)?(t(),n.push({t:"wide",s:r})):l+=r;t()},t=/`[^`]*`/g;let r,s=0;for(;null!==(r=t.exec(e));)l(e.slice(s,r.index)),n.push({t:"code",s:r[0]}),s=r.index+r[0].length;return l(e.slice(s)),n}(e)){if("space"===a.t){""!==t&&(s=!0);continue}const e=a.s,c=o("code"===a.t?e.replace(/`/g,""):e),g=s?1:0,i="atom"===a.t&&e.length<=2&&/^[.,;:!?)\]}…'"]+$/.test(e);""!==t&&!i&&r+g+c>n?(l.push(t),t=e,r=c,s=!1):(s&&(t+=" ",r+=1,s=!1),t+=e,r+=c)}return t&&l.push(t),l}const $=/\b(?:Common bug|Common mistake|Common error|Pitfall|Gotcha|Footgun)\b[::]?|\b(?:Warning|Caution)\b[::]|(?:常见错误|常见陷阱|常见误区|陷阱|注意|警告|误区)[::]/gi;function w(o,e){const n=e?chalk.bold.white:chalk.white;let l,t="",r=0;for($.lastIndex=0;null!==(l=$.exec(o));)l.index>r&&(t+=n(o.slice(r,l.index))),t+=chalk.bold.yellow(l[0]),r=l.index+l[0].length;return r<o.length&&(t+=n(o.slice(r))),t}function _(o,e=!1){let n="",l=0;const t=/`([^`]*)`/g;let r;for(;null!==(r=t.exec(o));)r.index>l&&(n+=w(o.slice(l,r.index),e)),n+=chalk.cyan(r[1]),l=r.index+r[0].length;return l<o.length&&(n+=w(o.slice(l),e)),n}function k(o,e=!1){let n=!1;for(const l of o.split("\n"))if(/^\s*```/.test(l))n=!n;else if(n)console.log(chalk.cyan(" │ ")+chalk.cyan(` ${l}`));else if(""!==l)if(f(l))console.log(chalk.cyan(" │ ")+chalk.white(l));else for(const o of p(l,60))console.log(chalk.cyan(" │ ")+` ${_(o,e)}`);else m()}function C(o,e){const n=[];for(const l of o.split("\n")){if(""===l){n.push("");continue}if(f(l)){n.push(l);continue}let o="";for(const t of l.split(" "))(o+" "+t).trim().length>e?(n.push(o.trim()),o=t):o=(o+" "+t).trim();o&&n.push(o)}return n}let x=!1;export function setLearnAiEnabled(o){x=o}let v=new Set;export function setLearnPqsViewCards(o){v=o}export function renderHonorWall(o){const e=a().startsWith("zh"),{totals:n,chapters:l}=o,t=n.gold+n.silver+n.bronze,r=(o,e)=>e>0?chalk.bold.white(`${o}×${e}`):chalk.gray(`${o}×0`);if(console.log(chalk.gray(" ─────────────────────────────────────────")),console.log(chalk.bold.white(e?" 🏅 奖牌墙 ":" 🏅 Medal wall ")+r("🥇",n.gold)+chalk.gray(" · ")+r("🥈",n.silver)+chalk.gray(" · ")+r("🥉",n.bronze)+chalk.gray(e?" (永久 — 挂上墙就不会摘下)":" (permanent — once earned, never removed)")),0===t){const n=null!=o.floor?`${o.floor}+`:e?"高分":"a high score";return console.log(chalk.gray(e?` 还没有奖牌 — 单卡提问评分 ${n} 并进入该卡前三,即可永久挂墙 🥇🥈🥉`:` No medals yet — score ${n} on a card and hold its top-3 to hang one 🥇🥈🥉`)),void console.log(chalk.gray(" ─────────────────────────────────────────"))}const s=l.map(o=>{const e=`Ch${o.chapter}`,n=(o.gold>0?`🥇${o.gold}`:"")+(o.silver>0?`🥈${o.silver}`:"")+(o.bronze>0?`🥉${o.bronze}`:"");return n?chalk.white(`${e} ${n}`):chalk.gray(`${e} —`)}).join(chalk.gray(" "));console.log(" "+s);const c=l.flatMap(o=>o.cards.map(e=>({...e,chapter:o.chapter})));c.sort((o,e)=>o.rank-e.rank||o.chapter-e.chapter||o.card-e.card);for(const o of c.slice(0,24)){const n=(o.since||"").slice(0,10);console.log(chalk.white(` ${o.icon} `)+chalk.gray(`Ch${o.chapter} · `)+chalk.white(e?`第 ${o.card} 张`:`card ${o.card}`)+chalk.gray(` · ${o.score}`)+(n?chalk.gray(e?` · 始于 ${n}`:` · since ${n}`):""))}c.length>24&&console.log(chalk.gray(e?` …还有 ${c.length-24} 枚`:` …and ${c.length-24} more`)),console.log(chalk.gray(" ─────────────────────────────────────────"))}export function renderKnowledgeCard(l,t){const g=a(),i=e(l,g);y(i,t),m();const d=p(i.title,60);for(const o of d)h(chalk.bold.yellow(o));h(chalk.gray("─".repeat(Math.min(Math.max(...d.map(e=>o(e))),c))));const b=!!(l.chart_ids&&l.chart_ids.length>0);if(b&&r(l.chart_ids.slice(0,1),t.charts,s()),i.key_point&&""!==i.key_point.trim()?(b||m(),function(o,e,n){const l=a().startsWith("zh"),t=s().cols,r="-".repeat(Math.min(c,Math.max(24,t-6))),g=l?"要点":"KEY POINT",i=l?`第 ${e} / ${n} 张`:`Card ${e} / ${n}`;console.log(chalk.cyan(" │ ")+chalk.green(r)),console.log(chalk.cyan(" │ ")+chalk.green(`${g} · ${i}`));for(const e of p(o,60))console.log(chalk.cyan(" │ ")+chalk.bold.green(" "+e));console.log(chalk.cyan(" │ ")+chalk.green(r)),m()}(i.key_point,i.number,t.totalCards)):b||m(),i.body.filter(o=>null!=o&&""!==o.trim()).forEach((o,e)=>{const n=0===e?o.match(/^([\s\S]*?(?:[.!?](?=\s|$)|[。!?]))(\s[\s\S]*)?$/):null;if(n&&n[1].trim().length<=180){k(n[1],!0);const o=n[2]?.trimStart();o&&k(o,!1)}else k(o,!1);m()}),i.icoaConnection){h(chalk.magenta(" "+n("icoa_connection",g))),h(chalk.gray(" "+"─".repeat(20)));for(const o of p(i.icoaConnection,60))h(" "+chalk.magenta(o));m()}if(l.check){const o=g.startsWith("zh"),e=l._zh?.checkStatement??l._zh?.check?.statement,n=o?e??l.check.statement:l.check.statement;h(chalk.cyan(" 🤔 "+(o?"请回答 (输 y 或 n):":"Quick comprehension check (type y or n):"))),h(chalk.gray(" "+"─".repeat(20)));for(const o of p(n,60))h(" "+chalk.white(o));m()}u(),function(o,e){const n=a().startsWith("zh"),l=s().cols,t="-".repeat(Math.min(c,Math.max(24,l-6))),r=chalk.gray(" "),g=[];o.check?(g.push(chalk.bold.green("[y]")+chalk.gray(n?" 是":" yes")),g.push(chalk.bold.green("[n]")+chalk.gray(n?" 否":" no"))):g.push(chalk.bold.green("[ok]")+chalk.gray(n?" 继续":" next")),x&&(g.push(chalk.bold.magenta("[/ai]")+chalk.gray(n?" AI导师":" AI tutor")),g.push(chalk.bold.magenta("[/submit]")+chalk.gray(n?" 提问评分":" prompt score")),v.has(o.number)&&g.push(chalk.bold.magenta("[/view]")+chalk.gray(n?" 榜单":" top-3"))),g.push(chalk.bold.yellow("[e]")+chalk.gray(n?" 报告问题":" report")),g.push(chalk.bold.cyan("[b]")+chalk.gray(n?" 上一张":" back")),g.push(chalk.bold.cyan("[card N]")+chalk.gray(n?" 跳转":" jump")),g.push(chalk.gray("[quit]")+chalk.gray(n?" 退出":" quit")),console.log(chalk.gray(" "+t)),console.log(chalk.gray(" "+(n?`第 ${o.number} / ${e.totalCards} 张`:`Card ${o.number} / ${e.totalCards}`))),console.log(" "+g.join(r)),console.log(chalk.gray(" "+t)),console.log(chalk.gray(" "+(n?"e = 报告本卡错误,不算作答;提交后回到本题继续":"e = report a problem with this card (not an answer); you return here after"))),console.log(chalk.gray(" "+(n?"更多: bookmark · status · /honors 奖牌墙 · lang zh/en":"more: bookmark · status · /honors · lang zh/en"))),console.log()}(l,t)}export function renderCheckFeedback(o,e,n,l=!1){const t=a().startsWith("zh");if(console.log(),n)console.log(chalk.green(" ✓ ")+chalk.white(t?"答对了。":"Correct."));else if(console.log(chalk.yellow(" ✗ ")+chalk.white(t?"记下了,继续。":"Noted — moving on.")),l){const e="y"===o.check?.answer?t?"是":"Yes":t?"否":"No";console.log(chalk.gray(" "+(t?"正确答案: ":"Answer: ")+e+". "+(t?"回看上面那段。":"Re-read the paragraph above.")))}const r=t?o._zh?.solution??o.solution:o.solution;if(r&&r.length>0){console.log(),console.log(chalk.cyan(" 📝 "+(t?"解答:":"Worked solution:")));for(const o of r){for(const e of p(o,60))console.log(chalk.white(" "+e));console.log()}}console.log()}function z(o){return[chalk.white(" "+(o?"编著:Charlie Zhu":"Created & edited by Charlie Zhu")),chalk.gray(" "+(o?"ICOA 2026 创始人 · 课程总架构师":"Founder & Chief Curriculum Architect · ICOA 2026"))]}export function renderPhaseComplete(o,e,n,l,r,s){const c=a().startsWith("zh"),[g,i]=n.cardRange,d=e.checkResults||{};let y=0,u=0;for(let o=g;o<=i;o++){const e=d[String(o)];e&&(y++,e.correct&&u++)}const h="-".repeat(48);console.log();for(const o of t(" "))console.log(o);console.log(),console.log(chalk.bold.yellow(" "+h)),console.log(chalk.bold.yellow(" "+(c?`阶段 ${n.number} 完成`:`Phase ${n.number} complete`))+chalk.gray(" · ")+chalk.white(n.name));const m=y>0&&u===y?chalk.bold.green:chalk.bold.white;console.log(chalk.gray(" "+(c?"理解检查得分: ":"comprehension score: "))+m(`${u}/${y}`)+chalk.gray(0===y?c?" (本阶段未作答 y/n)":" (no y/n answered)":""));const b=String(i).length;for(let e=g;e<=i;e++){const n=o.cards.find(o=>o.number===e);if(!n)continue;const l=n,t=l.title??l.badge??"",r=(c?l._zh?.title??l._zh?.badge??t:t)||"",s=null!=n.check,a=d[String(e)];let g;g=a?a.correct?chalk.green("✓"):chalk.red("✗"):s?chalk.gray("○"):chalk.gray("·");const i=p(r,54-b),y=i[0]+(i.length>1?"…":"");console.log(chalk.gray(` ${String(e).padStart(b)} `)+g+chalk.white(" "+y))}if(console.log(chalk.gray(c?" ✓ 答对 · ✗ 答错 · ○ 未作答 · 无 y/n 的卡不计分":" ✓ correct · ✗ wrong · ○ unanswered · cards without a y/n are unscored")),r){const o=o=>"green"===o?chalk.bold.green:"blue"===o?chalk.bold.cyan:"yellow"===o?chalk.bold.yellow:chalk.bold.red;console.log(chalk.bold.magenta(" ⭐ "+(c?"提问得分 (best): ":"Prompt scores (best): "))+(0===r.length?chalk.gray(c?"本阶段尚未提问 — 用 /ai 研究,再 /submit 评分":"none this phase — research with /ai, then /submit"):r.map(e=>chalk.gray((c?"卡":"#")+e.card+" ")+o(e.tier)(String(e.score))).join(chalk.gray(" · "))))}if(s?.configured&&s.contest){const o=s.contest,e=chalk.bold.cyan(" ⚖ "+(c?"判分赛: ":"Contest: "));o.passed?console.log(e+chalk.bold.green(`${o.best_ac}/${o.n} AC ✅ `+(c?"已过关":"passed"))):0===o.attempts?console.log(e+chalk.white(c?`${o.n} 题 · 过 ${o.need} 题解锁 — 输入 `:`${o.n} problems · AC ${o.need} to pass — type `)+chalk.bold.green("contest")+chalk.white(c?" 开始":" to start")):console.log(e+chalk.yellow(c?`最好 ${o.best_ac}/${o.n} AC(需 ${o.need})· 已试 ${o.attempts} 次 — 输入 `:`best ${o.best_ac}/${o.n} AC (need ${o.need}) · ${o.attempts} attempt(s) — type `)+chalk.bold.green("contest")+chalk.yellow(c?" 再战":" to retry"))}l&&console.log(chalk.gray(" "+(c?"下一阶段: ":"next: "))+chalk.bold.cyan(c?`阶段 ${l.number}`:`Phase ${l.number}`)+chalk.gray(" · ")+chalk.white(l.name)),console.log(chalk.bold.yellow(" "+h));for(const o of z(c))console.log(o)}export function renderCurriculumComplete(o,e){const n=a().startsWith("zh"),l=e.checkResults||{},r="-".repeat(48);let s=0,c=0;console.log();for(const o of t(" "))console.log(o);console.log(),console.log(chalk.bold.green(" "+r)),console.log(chalk.bold.green(" 🏆 "+(n?`${o.name} — 全部完成!`:`${o.name} — complete!`))),console.log(chalk.bold.green(" "+r));for(const e of o.modules){const[o,t]=e.cardRange;let r=0,a=0;for(let e=o;e<=t;e++){const o=l[String(e)];o&&(r++,o.correct&&a++)}s+=r,c+=a;const g=r>0&&a===r?chalk.green:chalk.white;console.log(chalk.gray(" "+(n?`阶段 ${e.number}`:`Phase ${e.number}`).padEnd(8))+g(`${a}/${r}`.padEnd(7))+chalk.gray(e.name))}console.log(chalk.bold.green(" "+r)),console.log(chalk.bold.green(" "+(n?"总分 / overall: ":"Overall: "))+chalk.bold.white(`${c}/${s}`)),console.log(chalk.bold.green(" "+r));for(const o of z(n))console.log(o);console.log()}export function renderMCQCard(o,l){const t=a(),r=e(o,t);y(r,l),m(),h(chalk.bold.yellow("🎯 "+r.title)),h(chalk.gray("─".repeat(Math.min(r.title.length+4,c)))),m();for(const o of C(r.question,60))h(" "+o);m();for(const o of["A","B","C","D"])h(chalk.cyan(` ${o}.`)+" "+chalk.white(r.options[o]));m(),u(),console.log(chalk.gray(" "+(t.startsWith("zh")?"输入 ":"Type "))+chalk.bold.green("A")+chalk.gray(" / ")+chalk.bold.green("B")+chalk.gray(" / ")+chalk.bold.green("C")+chalk.gray(" / ")+chalk.bold.green("D")+chalk.gray(" "+n("type_to_answer",t))),console.log()}export function renderMCQFeedback(o,l,t,r){const s=a(),c=e(o,s);console.log(),t?console.log(" "+chalk.bold.green(n("correct",s)+" ")+chalk.gray(`${n("one_point",s)} · ${l} = ${c.options[l]}`)):console.log(" "+chalk.bold.red(n("not_quite",s)+" ")+chalk.gray(`${n("you_chose",s)} ${l}; ${n("answer_is",s)} ${c.answer}.`)),console.log(),console.log(chalk.gray(" "+n("explanation",s)));for(const o of C(c.explanation,60))console.log(chalk.gray(" "+o));const g=Object.values(r.mcqResults),i=g.filter(o=>o.correct).length;console.log(),console.log(chalk.gray(" "+n("mcq_accuracy_so_far",s)+" ")+chalk.white(`${i}/${g.length}`)),console.log(),t||(console.log(chalk.gray(" "+n("press_ok_continue",s)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",s))),console.log())}export function renderPracticalCard(o,l){const t=a(),r=e(o,t);y(r,l),m(),h(chalk.bold.yellow("🛠 "+r.title)),h(chalk.gray("─".repeat(Math.min(r.title.length+4,c)))),m();for(const o of C(r.task,60))h(" "+o);if(r.starterCode){m(),h(chalk.gray(" "+n("starter_code",t))),m();for(const o of r.starterCode.split("\n"))h(" "+chalk.cyan(o))}m(),u(),console.log(chalk.gray(" "+n("try_in_sandbox",t)+" ")+chalk.bold.cyan("!python3")+chalk.gray(" "+n("drops_into_python",t))),console.log(chalk.gray(" "+n("when_done",t))),console.log(chalk.gray(" ")+chalk.bold.green("done")+chalk.gray(" "+n("done_desc",t))),console.log(chalk.gray(" ")+chalk.bold.yellow("skip")+chalk.gray(" "+n("skip_desc",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log()}export function renderPracticalSuccess(o){const l=a(),t=e(o,l);console.log(),console.log(" "+chalk.bold.green(n("practical_recorded",l))),console.log(),console.log(chalk.gray(" "+n("reference_answer",l)));for(const o of C(t.successHint,60))console.log(chalk.gray(" "+o));console.log(),console.log(chalk.gray(" "+n("press_ok_continue",l)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",l))),console.log()}export function renderSimDemoCard(o,l){const t=a(),r=e(o,t);y(r,l),m(),h(chalk.bold.yellow("🎬 "+r.title)),m();for(const o of C(r.description,60))h(" "+o);m(),h(chalk.gray(" "+n("sim_requires",t))),m(),u(),console.log(chalk.gray(" ")+chalk.bold.cyan("sim")+chalk.gray(" "+n("sim_launch_desc",t))),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",t))),console.log(chalk.gray(" bookmark "+n("bookmark_desc",t))),console.log(chalk.gray(" back "+n("back_desc",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",t))),console.log()}export function renderArenaTaskCard(o,n){const l=a(),t=l.startsWith("zh"),r=e(o,l);y(r,n),m(),h(chalk.bold.cyan("🏟 "+r.title)),h(chalk.gray(" "+r.origin+" · "+r.domain+" · "+r.metric)),m();for(const o of r.statement){for(const e of C(o,60))h(" "+e);m()}h(chalk.bold.green(t?" 核心要点 — baseline → 解法的关键变化":" CORE DELTA — baseline → solution"));for(const o of r.core_delta)for(const e of C("• "+o,58))h(" "+chalk.white(e));m(),h(chalk.gray((t?" 提交格式: ":" Submission: ")+r.submission_format)),r.hints&&h(chalk.gray(t?" 本题有 5 级提示 (a–e),进入 arena 后逐级解锁。":" 5 progressive hints (a–e) unlock inside the arena.")),m(),u(),console.log(chalk.gray(" ")+chalk.bold.cyan("arena "+r.task_id)+chalk.gray(t?" 动手做这道题(下载 / 提示 / 提交 / 榜单)":" open the arena (download / hints / submit / board)")),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(t?" 继续下一张":" continue to next")),console.log(chalk.gray(" back "+(t?"上一张":"previous"))),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(t?"退出":"exit"))),console.log()}export function renderComingSoonCard(o,n){const l=a(),t=l.startsWith("zh"),r=e(o,l);y(r,n),m(),h(chalk.bold.yellow("🔜 "+r.title)),m();const s=r.note||(t?"这道竞技场题目正在编写中,敬请期待。":"This arena task is coming soon.");for(const o of C(s,60))h(" "+chalk.gray(o));m(),u(),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(t?" 继续":" continue")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(t?"退出":"exit"))),console.log()}export function renderMilestone(o,l){const t=a(),r=e(o,t),s=o.number===l.totalCards;console.log(),console.log(chalk.bold.yellow(" ╭"+g("═"))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+chalk.white(" "+n("milestone_header",t))),console.log(chalk.bold.yellow(" ║"));const c=` ${r.badge} ${r.emoji}`;console.log(chalk.bold.yellow(" ║ ")+chalk.bold.green(c)),console.log(chalk.bold.yellow(" ║ ")+chalk.gray(" ─".repeat(Math.max(1,Math.floor(r.badge.length/2+2))))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("in_wild_corresponds",t)));for(const o of C(r.realWorldLevel,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("whats_next",t)));for(const o of C(r.unlockedNext,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ╰"+g("═"))),console.log(),s?(console.log(chalk.gray(" "+n("demo_complete",t))),console.log(),console.log(chalk.gray(" "+n("unlock_full",t)+" ")+chalk.white(n("curriculum_name",t))+chalk.gray(",")),console.log(chalk.gray(" "+n("contact_team_leader",t)+" ")+chalk.bold.yellow("EA")+chalk.gray(" "+n("learn_token",t))),console.log(chalk.gray(" "+n("or_email",t)+" ")+chalk.cyan("australia@icoa2026.au")+chalk.gray(" "+n("for_partnership",t))),console.log(),console.log(chalk.gray(" "+n("type_quit",t)+" ")+chalk.bold.green("quit")+chalk.gray(" "+n("to_exit",t)+" ")+chalk.bold.green("status")+chalk.gray(" "+n("for_dashboard",t)))):(console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",t))),console.log(chalk.gray(" status "+n("status_full",t))),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",t)))),console.log()}export function renderStatus(o,e){const l=a(),t=e.cardsCompleted.length,r=o.totalCards,s=Object.values(e.mcqResults),c=s.filter(o=>o.correct).length;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(n("status_title",l))+" "+chalk.cyan("─".repeat(25))),m(),h(" "+chalk.gray(n("total_progress",l)+" ")+i(t,r)+chalk.gray(` (${t}/${r})`)),h(" "+chalk.gray(n("streak",l)+" ")+d(e.streakDays)+chalk.gray(` (${n("longest",l)} ${e.longestStreak})`)),h(" "+chalk.gray(n("mcq_accuracy",l)+" ")+chalk.white(`${c}/${s.length}`)),h(" "+chalk.gray(n("practicals_done",l)+" ")+chalk.white(`${e.practicalsCompleted.length}`)),h(" "+chalk.gray(n("bookmarked",l)+" ")+chalk.white(`${e.bookmarks.length}`)),m();const g=n("module",l).replace(":","").trim();for(const n of o.modules){const[o,l]=n.cardRange,t=e.cardsCompleted.filter(e=>e>=o&&e<=l).length,r=l-o+1;h(" "+(t===r?chalk.green("✓"):t>0?chalk.yellow("▶"):chalk.gray("□"))+" "+chalk.gray(`${g} ${n.number}: `)+chalk.white(`${t}/${r}`)+chalk.gray(" "+n.name))}if(m(),e.achievements.length>0){h(" "+chalk.gray(n("achievements",l)));for(const o of e.achievements)h(" "+chalk.bold.yellow("★ ")+chalk.white(o))}else h(" "+chalk.gray(n("achievements_none",l)));m(),u()}
|
|
1
|
+
import chalk from"chalk";import o from"string-width";import{localized as e}from"./learn-curricula.js";import{t as n}from"./learn-i18n.js";import{getConfig as t}from"./config.js";import{icoaBannerLines as l}from"./banner.js";import{renderCharts as r,detectCaps as s}from"./render-card.js";function c(){return(t().language||"en").toLowerCase()}const a=64;function g(o="─"){return o.repeat(66)}function i(o,e,n=20){const t=e>0?o/e:0,l=Math.floor(t*n),r=n-l;return`${chalk.green("█".repeat(l))+chalk.gray("░".repeat(r))} ${(100*t).toFixed(1)}%`}function d(o){return o>=30?chalk.red("🔥🔥🔥 "+o+" days"):o>=7?chalk.yellow("🔥 "+o+" days"):o>=1?chalk.gray("· "+o+" day"+(o>1?"s":"")):chalk.gray("—")}export function renderWelcome(o,e,t){const l=c(),r=e.cardsCompleted.length,s=o.totalCards,a=s-r,y=o.modules.find(o=>e.currentCard>=o.cardRange[0]&&e.currentCard<=o.cardRange[1]),u=/demo/i.test(o.id);if(console.log(),console.log(chalk.cyan(" ╭"+g("═"))),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ║ ")+chalk.bold.white(" "+(o.name?.trim()||n("academy_title",l)))),console.log(chalk.cyan(" ║")),t)console.log(chalk.cyan(" ║ ")+chalk.white(" "+n(u?"welcome_new_demo":"welcome_new",l)));else{const o=new Date(e.lastSeenAt),t=Math.floor((Date.now()-o.getTime())/36e5),r=t<1?n("just_now",l):t<24?`${t}${n("ago_hours",l)}`:`${Math.floor(t/24)}${n("ago_days",l)}`;console.log(chalk.cyan(" ║ ")+chalk.white(` ${n("welcome_back",l)} ${r}`))}console.log(chalk.cyan(" ║"));const h=y?`${y.number}. ${y.name}`:"—";console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("module",l).padEnd(11))+chalk.white(h)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("progress",l).padEnd(11))+i(r,s)+chalk.gray(` (${r}/${s})`)),console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("streak",l).padEnd(11))+d(e.streakDays)),a>0&&e.currentCard<=s&&console.log(chalk.cyan(" ║ ")+" "+chalk.gray(n("next_card",l).padEnd(11))+chalk.white(`#${e.currentCard} ${n("of",l)} ${s}`)),console.log(chalk.cyan(" ║")),console.log(chalk.cyan(" ╰"+g("═"))),console.log(),console.log(chalk.gray(" ─────────────────────────────────────────────"));const b=0===r?n("continue_start",l):`${n("continue_resume",l)} ${e.currentCard}`;console.log(chalk.bold.green(" continue")+chalk.gray(" "+b)),console.log(chalk.yellow(" status")+chalk.gray(" "+n("status_full",l))),e.bookmarks.length>0&&console.log(chalk.yellow(" bookmarks")+chalk.gray(` ${e.bookmarks.length} ${n("bookmarks_desc",l)}`)),console.log(chalk.gray(" quit")+chalk.gray(" "+n("quit_desc",l))),console.log(chalk.gray(" ─────────────────────────────────────────────")),console.log()}function y(o,e){const t=c(),l=o.module,r=e.modules.find(o=>o.number===l),s=r?r.name:n("unknown_module",t),g=n("module",t).replace(":","").trim(),i=`${n("card",t)} ${o.number} / ${e.totalCards} · ${g} ${l} · ${s}`;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(i)+" "+chalk.cyan("─".repeat(Math.max(0,a-i.length-4))))}function u(){console.log(chalk.cyan(" ╰"+g())),console.log()}function h(o){console.log(chalk.cyan(" │ ")+chalk.white(o))}function b(){console.log(chalk.cyan(" │"))}const m=/[┌┐└┘├┤┬┴┼─│╔╗╚╝═║╠╣╦╩╬▓▒░█]/;function f(o){return/^(?: {4}|\t)/.test(o)||m.test(o)||/ {3,}/.test(o)}function p(e,n){const t=[];let l="",r=0,s=!1;for(const c of function(e){const n=[],t=e=>{let t="";const l=()=>{t&&(n.push({t:"atom",s:t}),t="")};for(const r of e)" "===r?(l(),n.push({t:"space"})):2===o(r)?(l(),n.push({t:"wide",s:r})):t+=r;l()},l=/`[^`]*`/g;let r,s=0;for(;null!==(r=l.exec(e));)t(e.slice(s,r.index)),n.push({t:"code",s:r[0]}),s=r.index+r[0].length;return t(e.slice(s)),n}(e)){if("space"===c.t){""!==l&&(s=!0);continue}const e=c.s,a=o("code"===c.t?e.replace(/`/g,""):e),g=s?1:0,i="atom"===c.t&&e.length<=2&&/^[.,;:!?)\]}…'"]+$/.test(e);""!==l&&!i&&r+g+a>n?(t.push(l),l=e,r=a,s=!1):(s&&(l+=" ",r+=1,s=!1),l+=e,r+=a)}return l&&t.push(l),t}const $=/\b(?:Common bug|Common mistake|Common error|Pitfall|Gotcha|Footgun)\b[::]?|\b(?:Warning|Caution)\b[::]|(?:常见错误|常见陷阱|常见误区|陷阱|注意|警告|误区)[::]/gi;function w(o,e){const n=e?chalk.bold.white:chalk.white;let t,l="",r=0;for($.lastIndex=0;null!==(t=$.exec(o));)t.index>r&&(l+=n(o.slice(r,t.index))),l+=chalk.bold.yellow(t[0]),r=t.index+t[0].length;return r<o.length&&(l+=n(o.slice(r))),l}function _(o,e=!1){let n="",t=0;const l=/`([^`]*)`/g;let r;for(;null!==(r=l.exec(o));)r.index>t&&(n+=w(o.slice(t,r.index),e)),n+=chalk.cyan(r[1]),t=r.index+r[0].length;return t<o.length&&(n+=w(o.slice(t),e)),n}function k(o,e=!1){let n=!1;for(const t of o.split("\n"))if(/^\s*```/.test(t))n=!n;else if(n)console.log(chalk.cyan(" │ ")+chalk.cyan(` ${t}`));else if(""!==t)if(f(t))console.log(chalk.cyan(" │ ")+chalk.white(t));else for(const o of p(t,60))console.log(chalk.cyan(" │ ")+` ${_(o,e)}`);else b()}function C(o,e){const n=[];for(const t of o.split("\n")){if(""===t){n.push("");continue}if(f(t)){n.push(t);continue}let o="";for(const l of t.split(" "))(o+" "+l).trim().length>e?(n.push(o.trim()),o=l):o=(o+" "+l).trim();o&&n.push(o)}return n}let x=!1;export function setLearnAiEnabled(o){x=o}let v=new Set;export function setLearnPqsViewCards(o){v=o}let z=new Map;export function setLearnHonorRanks(o){z=o}const M=["①","②","③","④","⑤","⑥","⑦","⑧","⑨","⑩"];export function renderHonorWall(o){const e=c().startsWith("zh"),{totals:n,chapters:t}=o,l=n.gold+n.silver+n.bronze,r=(o,e)=>e>0?chalk.bold.white(`${o}×${e}`):chalk.gray(`${o}×0`);if(console.log(chalk.gray(" ─────────────────────────────────────────")),console.log(chalk.bold.white(e?" 🏅 奖牌墙 ":" 🏅 Medal wall ")+r("🥇",n.gold)+chalk.gray(" · ")+r("🥈",n.silver)+chalk.gray(" · ")+r("🥉",n.bronze)+chalk.gray(e?" (永久 — 挂上墙就不会摘下)":" (permanent — once earned, never removed)")),0===l){const n=null!=o.floor?`${o.floor}+`:e?"高分":"a high score";return console.log(chalk.gray(e?` 还没有奖牌 — 单卡提问评分 ${n} 并进入该卡前三,即可永久挂墙 🥇🥈🥉`:` No medals yet — score ${n} on a card and hold its top-3 to hang one 🥇🥈🥉`)),void console.log(chalk.gray(" ─────────────────────────────────────────"))}const s=t.map(o=>{const e=`Ch${o.chapter}`,n=(o.gold>0?`🥇${o.gold}`:"")+(o.silver>0?`🥈${o.silver}`:"")+(o.bronze>0?`🥉${o.bronze}`:"");return n?chalk.white(`${e} ${n}`):chalk.gray(`${e} —`)}).join(chalk.gray(" "));console.log(" "+s);const a=t.flatMap(o=>o.cards.map(e=>({...e,chapter:o.chapter})));a.sort((o,e)=>o.rank-e.rank||o.chapter-e.chapter||o.card-e.card);for(const o of a.slice(0,24)){const n=(o.since||"").slice(0,10);console.log(chalk.white(` ${o.icon} `)+chalk.gray(`Ch${o.chapter} · `)+chalk.white(e?`第 ${o.card} 张`:`card ${o.card}`)+chalk.gray(` · ${o.score}`)+(n?chalk.gray(e?` · 始于 ${n}`:` · since ${n}`):""))}a.length>24&&console.log(chalk.gray(e?` …还有 ${a.length-24} 枚`:` …and ${a.length-24} more`)),console.log(chalk.gray(" ─────────────────────────────────────────"))}export function renderKnowledgeCard(t,l){const g=c(),i=e(t,g);y(i,l),b();const d=p(i.title,60);for(const o of d)h(chalk.bold.yellow(o));h(chalk.gray("─".repeat(Math.min(Math.max(...d.map(e=>o(e))),a))));const m=!!(t.chart_ids&&t.chart_ids.length>0);if(m&&r(t.chart_ids.slice(0,1),l.charts,s()),i.key_point&&""!==i.key_point.trim()?(m||b(),function(o,e,n){const t=c().startsWith("zh"),l=s().cols,r="-".repeat(Math.min(a,Math.max(24,l-6))),g=t?"要点":"KEY POINT",i=t?`第 ${e} / ${n} 张`:`Card ${e} / ${n}`;console.log(chalk.cyan(" │ ")+chalk.green(r)),console.log(chalk.cyan(" │ ")+chalk.green(`${g} · ${i}`));for(const e of p(o,60))console.log(chalk.cyan(" │ ")+chalk.bold.green(" "+e));console.log(chalk.cyan(" │ ")+chalk.green(r)),b()}(i.key_point,i.number,l.totalCards)):m||b(),i.body.filter(o=>null!=o&&""!==o.trim()).forEach((o,e)=>{const n=0===e?o.match(/^([\s\S]*?(?:[.!?](?=\s|$)|[。!?]))(\s[\s\S]*)?$/):null;if(n&&n[1].trim().length<=180){k(n[1],!0);const o=n[2]?.trimStart();o&&k(o,!1)}else k(o,!1);b()}),i.icoaConnection){h(chalk.magenta(" "+n("icoa_connection",g))),h(chalk.gray(" "+"─".repeat(20)));for(const o of p(i.icoaConnection,60))h(" "+chalk.magenta(o));b()}if(t.check){const o=g.startsWith("zh"),e=t._zh?.checkStatement??t._zh?.check?.statement,n=o?e??t.check.statement:t.check.statement;h(chalk.cyan(" 🤔 "+(o?"请回答 (输 y 或 n):":"Quick comprehension check (type y or n):"))),h(chalk.gray(" "+"─".repeat(20)));for(const o of p(n,60))h(" "+chalk.white(o));b()}u(),function(e,n){const t=c().startsWith("zh"),l=s().cols,r="-".repeat(Math.min(a,Math.max(24,l-6))),g=chalk.gray(" "),i=[];e.check?(i.push(chalk.bold.green("[y]")+chalk.gray(t?" 是":" yes")),i.push(chalk.bold.green("[n]")+chalk.gray(t?" 否":" no"))):i.push(chalk.bold.green("[ok]")+chalk.gray(t?" 继续":" next")),x&&(i.push(chalk.bold.magenta("[/ai]")+chalk.gray(t?" AI导师":" AI tutor")),i.push(chalk.bold.magenta("[/submit]")+chalk.gray(t?" 提问评分":" prompt score")));const d=z.get(e.number);var y;i.push(chalk.bold.magenta(d?`[/honors ${y=d,M[y-1]??`#${y}`}]`:"[/honors]")+chalk.gray(t?" 排行":" board")),i.push(chalk.bold.yellow("[e]")+chalk.gray(t?" 报告问题":" report")),i.push(chalk.bold.cyan("[b]")+chalk.gray(t?" 上一张":" back")),i.push(chalk.bold.cyan("[card N]")+chalk.gray(t?" 跳转":" jump")),i.push(chalk.gray("[quit]")+(t?chalk.gray(" 退出"):""));const u=o(r);console.log(chalk.gray(" "+r)),console.log(chalk.gray(" "+(t?`第 ${e.number} / ${n.totalCards} 张`:`Card ${e.number} / ${n.totalCards}`)));let h="",b=0;for(const e of i){const n=o(e);h&&b+2+n>u?(console.log(" "+h),h=e,b=n):(h=h?h+g+e:e,b=h===e?n:b+2+n)}h&&console.log(" "+h),console.log(chalk.gray(" "+r));const m=[t?"e = 报告本卡错误,不算作答;提交后回到本题继续":"e = report a card problem, not an answer — you return here after",t?"更多: bookmark · status · lang zh/en":"more: bookmark · status · lang zh/en"];for(const o of m)for(const e of p(o,u))console.log(chalk.gray(" "+e));console.log()}(t,l)}export function renderCheckFeedback(o,e,n,t=!1){const l=c().startsWith("zh");if(console.log(),n)console.log(chalk.green(" ✓ ")+chalk.white(l?"答对了。":"Correct."));else if(console.log(chalk.yellow(" ✗ ")+chalk.white(l?"记下了,继续。":"Noted — moving on.")),t){const e="y"===o.check?.answer?l?"是":"Yes":l?"否":"No";console.log(chalk.gray(" "+(l?"正确答案: ":"Answer: ")+e+". "+(l?"回看上面那段。":"Re-read the paragraph above.")))}const r=l?o._zh?.solution??o.solution:o.solution;if(r&&r.length>0){console.log(),console.log(chalk.cyan(" 📝 "+(l?"解答:":"Worked solution:")));for(const o of r){for(const e of p(o,60))console.log(chalk.white(" "+e));console.log()}}console.log()}function q(o){return[chalk.white(" "+(o?"编著:Charlie Zhu":"Created & edited by Charlie Zhu")),chalk.gray(" "+(o?"ICOA 2026 创始人 · 课程总架构师":"Founder & Chief Curriculum Architect · ICOA 2026"))]}export function renderPhaseComplete(o,e,n,t,r,s){const a=c().startsWith("zh"),[g,i]=n.cardRange,d=e.checkResults||{};let y=0,u=0;for(let o=g;o<=i;o++){const e=d[String(o)];e&&(y++,e.correct&&u++)}const h="-".repeat(48);console.log();for(const o of l(" "))console.log(o);console.log(),console.log(chalk.bold.yellow(" "+h)),console.log(chalk.bold.yellow(" "+(a?`阶段 ${n.number} 完成`:`Phase ${n.number} complete`))+chalk.gray(" · ")+chalk.white(n.name));const b=y>0&&u===y?chalk.bold.green:chalk.bold.white;console.log(chalk.gray(" "+(a?"理解检查得分: ":"comprehension score: "))+b(`${u}/${y}`)+chalk.gray(0===y?a?" (本阶段未作答 y/n)":" (no y/n answered)":""));const m=String(i).length;for(let e=g;e<=i;e++){const n=o.cards.find(o=>o.number===e);if(!n)continue;const t=n,l=t.title??t.badge??"",r=(a?t._zh?.title??t._zh?.badge??l:l)||"",s=null!=n.check,c=d[String(e)];let g;g=c?c.correct?chalk.green("✓"):chalk.red("✗"):s?chalk.gray("○"):chalk.gray("·");const i=p(r,54-m),y=i[0]+(i.length>1?"…":"");console.log(chalk.gray(` ${String(e).padStart(m)} `)+g+chalk.white(" "+y))}if(console.log(chalk.gray(a?" ✓ 答对 · ✗ 答错 · ○ 未作答 · 无 y/n 的卡不计分":" ✓ correct · ✗ wrong · ○ unanswered · cards without a y/n are unscored")),r){const o=o=>"green"===o?chalk.bold.green:"blue"===o?chalk.bold.cyan:"yellow"===o?chalk.bold.yellow:chalk.bold.red;console.log(chalk.bold.magenta(" ⭐ "+(a?"提问得分 (best): ":"Prompt scores (best): "))+(0===r.length?chalk.gray(a?"本阶段尚未提问 — 用 /ai 研究,再 /submit 评分":"none this phase — research with /ai, then /submit"):r.map(e=>chalk.gray((a?"卡":"#")+e.card+" ")+o(e.tier)(String(e.score))).join(chalk.gray(" · "))))}if(s?.configured&&s.contest){const o=s.contest;if(o.slots&&o.slots.length>0)for(const e of o.slots){const o=chalk.bold.cyan(` ⚔ ${a?"练习赛":"arena"} ${n.number}-${e.slot}: `);e.passed?console.log(o+chalk.bold.green(`${e.best_ac}/${e.n} AC ✅`)):0===e.attempts?console.log(o+chalk.white(a?`${e.n} 题 · 过 ${e.need} 题 — `:`${e.n} problems · AC ${e.need} — `)+chalk.bold.green(`contest ${e.slot}`)):console.log(o+chalk.yellow(a?`最好 ${e.best_ac}/${e.n}(需 ${e.need})· 已试 ${e.attempts} 次 — `:`best ${e.best_ac}/${e.n} (need ${e.need}) · ${e.attempts} attempt(s) — `)+chalk.bold.green(`contest ${e.slot}`))}else{const e=chalk.bold.cyan(" ⚖ "+(a?"判分赛: ":"Contest: "));o.passed?console.log(e+chalk.bold.green(`${o.best_ac}/${o.n} AC ✅ `+(a?"已过关":"passed"))):0===o.attempts?console.log(e+chalk.white(a?`${o.n} 题 · 过 ${o.need} 题解锁 — 输入 `:`${o.n} problems · AC ${o.need} to pass — type `)+chalk.bold.green("contest")+chalk.white(a?" 开始":" to start")):console.log(e+chalk.yellow(a?`最好 ${o.best_ac}/${o.n} AC(需 ${o.need})· 已试 ${o.attempts} 次 — 输入 `:`best ${o.best_ac}/${o.n} AC (need ${o.need}) · ${o.attempts} attempt(s) — type `)+chalk.bold.green("contest")+chalk.yellow(a?" 再战":" to retry"))}}t&&console.log(chalk.gray(" "+(a?"下一阶段: ":"next: "))+chalk.bold.cyan(a?`阶段 ${t.number}`:`Phase ${t.number}`)+chalk.gray(" · ")+chalk.white(t.name)),console.log(chalk.bold.yellow(" "+h));for(const o of q(a))console.log(o)}export function renderCurriculumComplete(o,e){const n=c().startsWith("zh"),t=e.checkResults||{},r="-".repeat(48);let s=0,a=0;console.log();for(const o of l(" "))console.log(o);console.log(),console.log(chalk.bold.green(" "+r)),console.log(chalk.bold.green(" 🏆 "+(n?`${o.name} — 全部完成!`:`${o.name} — complete!`))),console.log(chalk.bold.green(" "+r));for(const e of o.modules){const[o,l]=e.cardRange;let r=0,c=0;for(let e=o;e<=l;e++){const o=t[String(e)];o&&(r++,o.correct&&c++)}s+=r,a+=c;const g=r>0&&c===r?chalk.green:chalk.white;console.log(chalk.gray(" "+(n?`阶段 ${e.number}`:`Phase ${e.number}`).padEnd(8))+g(`${c}/${r}`.padEnd(7))+chalk.gray(e.name))}console.log(chalk.bold.green(" "+r)),console.log(chalk.bold.green(" "+(n?"总分 / overall: ":"Overall: "))+chalk.bold.white(`${a}/${s}`)),console.log(chalk.bold.green(" "+r));for(const o of q(n))console.log(o);console.log()}export function renderMCQCard(o,t){const l=c(),r=e(o,l);y(r,t),b(),h(chalk.bold.yellow("🎯 "+r.title)),h(chalk.gray("─".repeat(Math.min(r.title.length+4,a)))),b();for(const o of C(r.question,60))h(" "+o);b();for(const o of["A","B","C","D"])h(chalk.cyan(` ${o}.`)+" "+chalk.white(r.options[o]));b(),u(),console.log(chalk.gray(" "+(l.startsWith("zh")?"输入 ":"Type "))+chalk.bold.green("A")+chalk.gray(" / ")+chalk.bold.green("B")+chalk.gray(" / ")+chalk.bold.green("C")+chalk.gray(" / ")+chalk.bold.green("D")+chalk.gray(" "+n("type_to_answer",l))),console.log(chalk.gray(" ")+chalk.bold.cyan("back")+chalk.gray(l.startsWith("zh")?" 上一张 · ":" previous card · ")+chalk.bold.cyan("card N")+chalk.gray(l.startsWith("zh")?" 跳转":" jump")),console.log()}export function renderMCQFeedback(o,t,l,r){const s=c(),a=e(o,s);console.log(),l?console.log(" "+chalk.bold.green(n("correct",s)+" ")+chalk.gray(`${n("one_point",s)} · ${t} = ${a.options[t]}`)):console.log(" "+chalk.bold.red(n("not_quite",s)+" ")+chalk.gray(`${n("you_chose",s)} ${t}; ${n("answer_is",s)} ${a.answer}.`)),console.log(),console.log(chalk.gray(" "+n("explanation",s)));for(const o of C(a.explanation,60))console.log(chalk.gray(" "+o));const g=Object.values(r.mcqResults),i=g.filter(o=>o.correct).length;console.log(),console.log(chalk.gray(" "+n("mcq_accuracy_so_far",s)+" ")+chalk.white(`${i}/${g.length}`)),console.log(),l||(console.log(chalk.gray(" "+n("press_ok_continue",s)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",s))),console.log())}export function renderPracticalCard(o,t){const l=c(),r=e(o,l);y(r,t),b(),h(chalk.bold.yellow("🛠 "+r.title)),h(chalk.gray("─".repeat(Math.min(r.title.length+4,a)))),b();for(const o of C(r.task,60))h(" "+o);if(r.starterCode){b(),h(chalk.gray(" "+n("starter_code",l))),b();for(const o of r.starterCode.split("\n"))h(" "+chalk.cyan(o))}b(),u(),console.log(chalk.gray(" "+n("try_in_sandbox",l)+" ")+chalk.bold.cyan("!python3")+chalk.gray(" "+n("drops_into_python",l))),console.log(chalk.gray(" "+n("when_done",l))),console.log(chalk.gray(" ")+chalk.bold.green("done")+chalk.gray(" "+n("done_desc",l))),console.log(chalk.gray(" ")+chalk.bold.yellow("skip")+chalk.gray(" "+n("skip_desc",l))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log()}export function renderPracticalSuccess(o){const t=c(),l=e(o,t);console.log(),console.log(" "+chalk.bold.green(n("practical_recorded",t))),console.log(),console.log(chalk.gray(" "+n("reference_answer",t)));for(const o of C(l.successHint,60))console.log(chalk.gray(" "+o));console.log(),console.log(chalk.gray(" "+n("press_ok_continue",t)+" ")+chalk.bold.green("ok")+chalk.gray(" "+n("to_continue",t))),console.log()}export function renderSimDemoCard(o,t){const l=c(),r=e(o,l);y(r,t),b(),h(chalk.bold.yellow("🎬 "+r.title)),b();for(const o of C(r.description,60))h(" "+o);b(),h(chalk.gray(" "+n("sim_requires",l))),b(),u(),console.log(chalk.gray(" ")+chalk.bold.cyan("sim")+chalk.gray(" "+n("sim_launch_desc",l))),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",l))),console.log(chalk.gray(" bookmark "+n("bookmark_desc",l))),console.log(chalk.gray(" back "+n("back_desc",l))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",l))),console.log()}export function renderArenaTaskCard(o,n){const t=c(),l=t.startsWith("zh"),r=e(o,t);y(r,n),b(),h(chalk.bold.cyan("🏟 "+r.title)),h(chalk.gray(" "+r.origin+" · "+r.domain+" · "+r.metric)),b();for(const o of r.statement){for(const e of C(o,60))h(" "+e);b()}h(chalk.bold.green(l?" 核心要点 — baseline → 解法的关键变化":" CORE DELTA — baseline → solution"));for(const o of r.core_delta)for(const e of C("• "+o,58))h(" "+chalk.white(e));b(),h(chalk.gray((l?" 提交格式: ":" Submission: ")+r.submission_format)),r.hints&&h(chalk.gray(l?" 本题有 5 级提示 (a–e),进入 arena 后逐级解锁。":" 5 progressive hints (a–e) unlock inside the arena.")),b(),u(),console.log(chalk.gray(" ")+chalk.bold.cyan("arena "+r.task_id)+chalk.gray(l?" 动手做这道题(下载 / 提示 / 提交 / 榜单)":" open the arena (download / hints / submit / board)")),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(l?" 继续下一张":" continue to next")),console.log(chalk.gray(" back "+(l?"上一张":"previous"))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(l?"退出":"exit"))),console.log()}export function renderComingSoonCard(o,n){const t=c(),l=t.startsWith("zh"),r=e(o,t);y(r,n),b(),h(chalk.bold.yellow("🔜 "+r.title)),b();const s=r.note||(l?"这道竞技场题目正在编写中,敬请期待。":"This arena task is coming soon.");for(const o of C(s,60))h(" "+chalk.gray(o));b(),u(),console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(l?" 继续":" continue")),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+(l?"退出":"exit"))),console.log()}export function renderMilestone(o,t){const l=c(),r=e(o,l),s=o.number===t.totalCards;console.log(),console.log(chalk.bold.yellow(" ╭"+g("═"))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+chalk.white(" "+n("milestone_header",l))),console.log(chalk.bold.yellow(" ║"));const a=` ${r.badge} ${r.emoji}`;console.log(chalk.bold.yellow(" ║ ")+chalk.bold.green(a)),console.log(chalk.bold.yellow(" ║ ")+chalk.gray(" ─".repeat(Math.max(1,Math.floor(r.badge.length/2+2))))),console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("in_wild_corresponds",l)));for(const o of C(r.realWorldLevel,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ║ ")+" "+chalk.gray(n("whats_next",l)));for(const o of C(r.unlockedNext,60))console.log(chalk.bold.yellow(" ║ ")+" "+chalk.white(o));console.log(chalk.bold.yellow(" ║")),console.log(chalk.bold.yellow(" ╰"+g("═"))),console.log(),s?(console.log(chalk.gray(" "+n("demo_complete",l))),console.log(),console.log(chalk.gray(" "+n("unlock_full",l)+" ")+chalk.white(n("curriculum_name",l))+chalk.gray(",")),console.log(chalk.gray(" "+n("contact_team_leader",l)+" ")+chalk.bold.yellow("EA")+chalk.gray(" "+n("learn_token",l))),console.log(chalk.gray(" "+n("or_email",l)+" ")+chalk.cyan("australia@icoa2026.au")+chalk.gray(" "+n("for_partnership",l))),console.log(),console.log(chalk.gray(" "+n("type_quit",l)+" ")+chalk.bold.green("quit")+chalk.gray(" "+n("to_exit",l)+" ")+chalk.bold.green("status")+chalk.gray(" "+n("for_dashboard",l)))):(console.log(chalk.gray(" ")+chalk.bold.green("ok")+chalk.gray(" / ")+chalk.bold.green("next")+chalk.gray(" "+n("continue_to_next",l))),console.log(chalk.gray(" status "+n("status_full",l))),console.log(chalk.gray(" back previous card")),console.log(chalk.gray(" card N jump to a card by number")),console.log(chalk.gray(" quit "+n("quit_desc",l)))),console.log()}export function renderStatus(o,e){const t=c(),l=e.cardsCompleted.length,r=o.totalCards,s=Object.values(e.mcqResults),a=s.filter(o=>o.correct).length;console.log(),console.log(chalk.cyan(" ╭─ ")+chalk.bold.white(n("status_title",t))+" "+chalk.cyan("─".repeat(25))),b(),h(" "+chalk.gray(n("total_progress",t)+" ")+i(l,r)+chalk.gray(` (${l}/${r})`)),h(" "+chalk.gray(n("streak",t)+" ")+d(e.streakDays)+chalk.gray(` (${n("longest",t)} ${e.longestStreak})`)),h(" "+chalk.gray(n("mcq_accuracy",t)+" ")+chalk.white(`${a}/${s.length}`)),h(" "+chalk.gray(n("practicals_done",t)+" ")+chalk.white(`${e.practicalsCompleted.length}`)),h(" "+chalk.gray(n("bookmarked",t)+" ")+chalk.white(`${e.bookmarks.length}`)),b();const g=n("module",t).replace(":","").trim();for(const n of o.modules){const[o,t]=n.cardRange,l=e.cardsCompleted.filter(e=>e>=o&&e<=t).length,r=t-o+1;h(" "+(l===r?chalk.green("✓"):l>0?chalk.yellow("▶"):chalk.gray("□"))+" "+chalk.gray(`${g} ${n.number}: `)+chalk.white(`${l}/${r}`)+chalk.gray(" "+n.name))}if(b(),e.achievements.length>0){h(" "+chalk.gray(n("achievements",t)));for(const o of e.achievements)h(" "+chalk.bold.yellow("★ ")+chalk.white(o))}else h(" "+chalk.gray(n("achievements_none",t)));b(),u()}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "icoa-cli",
|
|
3
|
-
"version": "2.19.
|
|
3
|
+
"version": "2.19.404",
|
|
4
4
|
"description": "ICOA CLI — The world's first CLI-native cyber & AI security olympiad terminal: AI4CTF (Day 1), CTF4AI (Day 2), VLA4CTF (Pioneer Round — embodied AI)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|