chofex-cli 0.1.172 → 0.1.177

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.
Files changed (3) hide show
  1. package/README.md +6 -1
  2. package/dist/index.js +549 -49
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -24,6 +24,11 @@ chofex challenge notebook
24
24
  chofex challenge test --source ./shipping.js
25
25
  chofex challenge evaluate --source ./shipping.js
26
26
  chofex challenge ranking
27
+ chofex challenge init --challenge broken-agent
28
+ cd broken-agent && npm test
29
+ chofex challenge test --challenge broken-agent --source ./scheduler.js
30
+ chofex challenge evaluate --challenge broken-agent --source ./scheduler.js
31
+ chofex challenge ranking --challenge broken-agent
27
32
  chofex confirm
28
33
  ```
29
34
 
@@ -34,7 +39,7 @@ to the latest published version.
34
39
  optional phone number, bio, portfolio URL, shipped project, LinkedIn and GitHub
35
40
  URLs, and Terms and Conditions. Use `--input` to submit a completed JSON
36
41
  application.
37
- The Black Box does not block submission, but every challenge winner must submit
42
+ The technical challenges do not block submission, but every challenge winner must submit
38
43
  an application before being accepted. Top results receive a direct pass; other
39
44
  scores give organizers another review metric.
40
45
 
package/dist/index.js CHANGED
@@ -3053,17 +3053,17 @@ var require_exec = __commonJS((exports, module) => {
3053
3053
  var import_node_path = __toESM2(__require("node:path"));
3054
3054
  var import_execa = __toESM2(require_execa());
3055
3055
  var import_envpath = require_envpath();
3056
- var import_errors8 = require_errors();
3056
+ var import_errors9 = require_errors();
3057
3057
  var import_lookup = require_lookup();
3058
3058
  async function execVercelCli2(args2, options = {}) {
3059
3059
  const cwd = import_node_path.default.resolve(options.cwd ?? process.cwd());
3060
- await (0, import_errors8.assertValidCwd)(cwd);
3060
+ await (0, import_errors9.assertValidCwd)(cwd);
3061
3061
  const env = mergeExecEnv(options.env);
3062
3062
  const pathValue = (0, import_envpath.getEnvPath)(env);
3063
3063
  try {
3064
3064
  return await execResolvedVercelCli(args2, options, cwd, env, pathValue);
3065
3065
  } catch (error2) {
3066
- if (error2 instanceof import_errors8.VercelCliError && error2.code === "VERCEL_CLI_NOT_FOUND") {
3066
+ if (error2 instanceof import_errors9.VercelCliError && error2.code === "VERCEL_CLI_NOT_FOUND") {
3067
3067
  (0, import_lookup.clearCachedCliInvocation)(cwd, pathValue);
3068
3068
  return await execResolvedVercelCli(args2, options, cwd, env, pathValue);
3069
3069
  }
@@ -3090,15 +3090,15 @@ var require_exec = __commonJS((exports, module) => {
3090
3090
  const { stdout, stderr } = await (0, import_execa.default)(invocation.command, [...invocation.commandArgs, ...args2], execaOptions);
3091
3091
  return { stdout, stderr, invocation };
3092
3092
  } catch (error2) {
3093
- throw (0, import_errors8.toVercelCliError)(invocation, error2);
3093
+ throw (0, import_errors9.toVercelCliError)(invocation, error2);
3094
3094
  }
3095
3095
  }
3096
3096
  async function resolveInvocationOrThrow(cwd, pathValue) {
3097
3097
  const resolution = await (0, import_lookup.resolveCachedCliInvocation)(cwd, pathValue);
3098
3098
  if (!resolution.found) {
3099
- throw new import_errors8.VercelCliError({
3099
+ throw new import_errors9.VercelCliError({
3100
3100
  code: "VERCEL_CLI_NOT_FOUND",
3101
- message: (0, import_errors8.getCliNotFoundMessage)(resolution.diagnostics)
3101
+ message: (0, import_errors9.getCliNotFoundMessage)(resolution.diagnostics)
3102
3102
  });
3103
3103
  }
3104
3104
  return (0, import_lookup.toVercelCliInvocation)(resolution);
@@ -3139,13 +3139,13 @@ var require_dist = __commonJS((exports, module) => {
3139
3139
  var __toCommonJS = (mod) => __copyProps(__defProp2({}, "__esModule", { value: true }), mod);
3140
3140
  var src_exports = {};
3141
3141
  __export2(src_exports, {
3142
- VercelCliError: () => import_errors8.VercelCliError,
3142
+ VercelCliError: () => import_errors9.VercelCliError,
3143
3143
  clearVercelCliLookupCache: () => import_lookup.clearVercelCliLookupCache,
3144
3144
  execVercelCli: () => import_exec.execVercelCli,
3145
3145
  findVercelCli: () => import_lookup.findVercelCli
3146
3146
  });
3147
3147
  module.exports = __toCommonJS(src_exports);
3148
- var import_errors8 = require_errors();
3148
+ var import_errors9 = require_errors();
3149
3149
  var import_exec = require_exec();
3150
3150
  var import_lookup = require_lookup();
3151
3151
  });
@@ -58552,6 +58552,7 @@ var runWith2 = (command, config) => {
58552
58552
  };
58553
58553
  // ../../packages/challenges-contract/src/index.ts
58554
58554
  var blackBoxChallengeSlug = "black-box";
58555
+ var brokenAgentChallengeSlug = "broken-agent";
58555
58556
  var challengeCatalog = [
58556
58557
  {
58557
58558
  slug: blackBoxChallengeSlug,
@@ -58564,6 +58565,7 @@ var challengeCatalog = [
58564
58565
  format: "accuracy",
58565
58566
  formatLabel: "Accuracy score",
58566
58567
  opensAt: "2026-09-17T14:00:00.000Z",
58568
+ closesAt: "2026-09-24T17:20:00.000Z",
58567
58569
  rankingVisibleAt: "2026-09-23T20:00:00.000Z",
58568
58570
  queryLimit: 25,
58569
58571
  evaluationLimit: 3,
@@ -58572,21 +58574,21 @@ var challengeCatalog = [
58572
58574
  solutionKind: "javascript_source"
58573
58575
  },
58574
58576
  {
58575
- slug: "last-mile",
58577
+ slug: brokenAgentChallengeSlug,
58576
58578
  number: 2,
58577
58579
  code: "02",
58578
- theme: "Last Mile",
58579
- title: "The Delivery Plan",
58580
- summary: "Build a delivery plan under capacity, traffic, and deadline constraints. Better heuristics score higher.",
58581
- coreSkill: "Optimization & heuristics",
58582
- format: "optimization",
58583
- formatLabel: "Optimization score",
58580
+ theme: "Broken Agent",
58581
+ title: "The Scheduler",
58582
+ summary: "Take a plausible AI-generated job scheduler whose public tests already pass and harden it against production reality.",
58583
+ coreSkill: "Production correctness & reliability",
58584
+ format: "accuracy",
58585
+ formatLabel: "Production readiness score",
58584
58586
  opensAt: "2026-09-25T05:00:00.000Z",
58585
58587
  queryLimit: 0,
58586
- evaluationLimit: 3,
58587
- hiddenSampleSize: 1,
58588
- playable: false,
58589
- solutionKind: "json_plan"
58588
+ evaluationLimit: 5,
58589
+ hiddenSampleSize: 100,
58590
+ playable: true,
58591
+ solutionKind: "javascript_source"
58590
58592
  },
58591
58593
  {
58592
58594
  slug: "make-it-fast",
@@ -58642,6 +58644,18 @@ var challengeCatalog = [
58642
58644
  ];
58643
58645
  var playableChallenges = challengeCatalog.filter((challenge) => challenge.playable);
58644
58646
  var challengeBySlug = (slug) => challengeCatalog.find((challenge) => challenge.slug === slug);
58647
+ var isChallengeOpenAt = (challenge, now3, forceOpen = false) => {
58648
+ if (forceOpen && challenge.playable)
58649
+ return true;
58650
+ if (now3.getTime() < Date.parse(challenge.opensAt))
58651
+ return false;
58652
+ return !isChallengeClosedAt(challenge, now3);
58653
+ };
58654
+ var isChallengeClosedAt = (challenge, now3) => {
58655
+ if (!challenge.closesAt)
58656
+ return false;
58657
+ return now3.getTime() >= Date.parse(challenge.closesAt);
58658
+ };
58645
58659
  var isChallengeRankingVisibleAt = (challenge, now3) => {
58646
58660
  if (!challenge.rankingVisibleAt)
58647
58661
  return true;
@@ -58677,7 +58691,7 @@ var formatChallengeOpeningInPeru = (opensAt) => {
58677
58691
  var challengeOpeningNotice = (title, opensAt) => `${title} opens ${formatChallengeOpeningInPeru(opensAt)}. Queries and evaluations are disabled until then; no attempts will be consumed.`;
58678
58692
  var ChallengeSlugSchema = exports_Schema.Literals([
58679
58693
  "black-box",
58680
- "last-mile",
58694
+ "broken-agent",
58681
58695
  "make-it-fast",
58682
58696
  "power-grid",
58683
58697
  "agent-arena"
@@ -58705,7 +58719,38 @@ var ChallengeScoreSchema = exports_Schema.Struct({
58705
58719
  sampleSize: exports_Schema.Number,
58706
58720
  meanError: exports_Schema.Number,
58707
58721
  queriesUsed: exports_Schema.Number,
58708
- runtimeMs: exports_Schema.Number
58722
+ runtimeMs: exports_Schema.Number,
58723
+ evaluationsUsed: exports_Schema.optional(exports_Schema.Number),
58724
+ breakdown: exports_Schema.optional(exports_Schema.Struct({
58725
+ coreBehavior: exports_Schema.Struct({
58726
+ earned: exports_Schema.Number,
58727
+ available: exports_Schema.Number
58728
+ }),
58729
+ persistence: exports_Schema.Struct({
58730
+ earned: exports_Schema.Number,
58731
+ available: exports_Schema.Number
58732
+ }),
58733
+ concurrency: exports_Schema.Struct({
58734
+ earned: exports_Schema.Number,
58735
+ available: exports_Schema.Number
58736
+ }),
58737
+ failureRecovery: exports_Schema.Struct({
58738
+ earned: exports_Schema.Number,
58739
+ available: exports_Schema.Number
58740
+ }),
58741
+ idempotency: exports_Schema.Struct({
58742
+ earned: exports_Schema.Number,
58743
+ available: exports_Schema.Number
58744
+ }),
58745
+ regressionSafety: exports_Schema.Struct({
58746
+ earned: exports_Schema.Number,
58747
+ available: exports_Schema.Number
58748
+ }),
58749
+ performance: exports_Schema.Struct({
58750
+ earned: exports_Schema.Number,
58751
+ available: exports_Schema.Number
58752
+ })
58753
+ }))
58709
58754
  });
58710
58755
  var ChallengeProgressStatus = exports_Schema.Literals([
58711
58756
  "not_started",
@@ -58718,6 +58763,7 @@ var ParticipantChallengeProgressSchema = exports_Schema.Struct({
58718
58763
  theme: exports_Schema.String,
58719
58764
  status: ChallengeProgressStatus,
58720
58765
  open: exports_Schema.Boolean,
58766
+ closed: exports_Schema.optional(exports_Schema.Boolean),
58721
58767
  playable: exports_Schema.Boolean,
58722
58768
  queriesUsed: exports_Schema.Number,
58723
58769
  queriesLimit: exports_Schema.Number,
@@ -58752,11 +58798,13 @@ var ChallengeCatalogItemSchema = exports_Schema.Struct({
58752
58798
  ]),
58753
58799
  formatLabel: exports_Schema.String,
58754
58800
  opensAt: exports_Schema.String,
58801
+ closesAt: exports_Schema.optional(exports_Schema.String),
58755
58802
  rankingVisibleAt: exports_Schema.optional(exports_Schema.String),
58756
58803
  queryLimit: exports_Schema.Number,
58757
58804
  evaluationLimit: exports_Schema.Number,
58758
58805
  playable: exports_Schema.Boolean,
58759
58806
  open: exports_Schema.Boolean,
58807
+ closed: exports_Schema.optional(exports_Schema.Boolean),
58760
58808
  rankingPath: exports_Schema.String
58761
58809
  });
58762
58810
  var ChallengeEvaluationSummarySchema = exports_Schema.Struct({
@@ -58769,7 +58817,8 @@ var ChallengeEvaluationSummarySchema = exports_Schema.Struct({
58769
58817
  shareCode: exports_Schema.String,
58770
58818
  rank: exports_Schema.optional(exports_Schema.Number),
58771
58819
  percentile: exports_Schema.optional(exports_Schema.Number),
58772
- createdAt: exports_Schema.String
58820
+ createdAt: exports_Schema.String,
58821
+ breakdown: exports_Schema.optional(ChallengeScoreSchema.fields.breakdown)
58773
58822
  });
58774
58823
  var ChallengeAttemptViewSchema = exports_Schema.Struct({
58775
58824
  challenge: ChallengeCatalogItemSchema,
@@ -58786,6 +58835,7 @@ var ChallengeQueryResultSchema = exports_Schema.Struct({
58786
58835
  queriesLimit: exports_Schema.Number
58787
58836
  });
58788
58837
  var ChallengeLocalTestResultSchema = exports_Schema.Struct({
58838
+ kind: exports_Schema.optional(exports_Schema.Literals(["black_box", "broken_agent"])),
58789
58839
  matchedObservations: exports_Schema.Number,
58790
58840
  observationCount: exports_Schema.Number,
58791
58841
  accuracy: exports_Schema.Number,
@@ -58811,7 +58861,8 @@ var ChallengeEvaluationResultSchema = exports_Schema.Struct({
58811
58861
  evaluationsRemaining: exports_Schema.Number,
58812
58862
  evaluationsLimit: exports_Schema.Number,
58813
58863
  rankingPath: exports_Schema.String,
58814
- shareText: exports_Schema.String
58864
+ shareText: exports_Schema.String,
58865
+ breakdown: exports_Schema.optional(ChallengeScoreSchema.fields.breakdown)
58815
58866
  });
58816
58867
  var ChallengeRankingEntrySchema = exports_Schema.Struct({
58817
58868
  rank: exports_Schema.Number,
@@ -59693,6 +59744,7 @@ var getChallengeRanking = (options, slug) => publicRequest(options, `/api/v1/cha
59693
59744
 
59694
59745
  // src/challenge-input.ts
59695
59746
  import { readFile as readFile3 } from "node:fs/promises";
59747
+ var defaultChallengeSlug = "black-box";
59696
59748
  var readStdin = async () => {
59697
59749
  const chunks = [];
59698
59750
  for await (const chunk of process.stdin) {
@@ -59708,9 +59760,12 @@ var readTextFile = (path4) => exports_Effect.tryPromise({
59708
59760
  },
59709
59761
  catch: (error2) => cliError("INVALID_INPUT_FILE", `Could not read ${path4}: ${String(error2)}`)
59710
59762
  });
59711
- var javascriptSourceFromPath = (path4) => {
59763
+ var javascriptSourceFromPath = (path4, challenge = defaultChallengeSlug) => {
59712
59764
  if (!path4) {
59713
- return exports_Effect.fail(cliError("SOURCE_REQUIRED", "Pass --source <file.js> with function calculateShipping(input)"));
59765
+ let expectedFunction = "calculateShipping(input)";
59766
+ if (challenge === "broken-agent")
59767
+ expectedFunction = "createScheduler(dependencies)";
59768
+ return exports_Effect.fail(cliError("SOURCE_REQUIRED", `Pass --source <file.js> with function ${expectedFunction}`));
59714
59769
  }
59715
59770
  return readTextFile(path4).pipe(exports_Effect.map((source) => ({ kind: "javascript_source", source })));
59716
59771
  };
@@ -59762,7 +59817,6 @@ var shipmentInput = (path4, flags) => {
59762
59817
  }
59763
59818
  return interactiveShipment();
59764
59819
  };
59765
- var defaultChallengeSlug = "black-box";
59766
59820
 
59767
59821
  // src/brand.ts
59768
59822
  var eventName = "Hack the Andes";
@@ -59782,20 +59836,35 @@ var challengeLaunchNotice = (title, opensAt, now3 = new Date) => {
59782
59836
  return;
59783
59837
  return challengeOpeningNotice(title, opensAt);
59784
59838
  };
59839
+ var challengeParticipationNotice = (title, opensAt, closesAt, now3 = new Date) => {
59840
+ if (closesAt && now3.getTime() >= Date.parse(closesAt)) {
59841
+ return `${title} está cerrado. Las consultas, pruebas locales y evaluaciones oficiales están deshabilitadas.`;
59842
+ }
59843
+ return challengeLaunchNotice(title, opensAt, now3);
59844
+ };
59785
59845
  var challengeListText = (catalog) => {
59786
59846
  const lines2 = [`${eventName} challenges`, ""];
59787
59847
  for (const challenge of catalog.challenges) {
59788
- let state = "open";
59789
- if (!challenge.open) {
59790
- state = `opens ${formatChallengeOpeningInPeru(challenge.opensAt)}`;
59791
- }
59792
- const playable = challenge.playable ? "" : " (coming later)";
59848
+ let state = `abre ${formatChallengeOpeningInPeru(challenge.opensAt)}`;
59849
+ if (challenge.closed) {
59850
+ state = "cerrado";
59851
+ } else if (challenge.open && challenge.playable) {
59852
+ state = "abierto";
59853
+ } else if (!challenge.playable) {
59854
+ state = "próximamente";
59855
+ }
59856
+ const playable = challenge.playable ? "" : " (próximamente)";
59793
59857
  lines2.push(`${challenge.code} ${challenge.theme} — ${challenge.title}${playable}`);
59794
59858
  lines2.push(` ${challenge.coreSkill} · ${challenge.formatLabel} · ${state}`);
59795
59859
  lines2.push(` Ranking: ${challenge.rankingPath}`);
59796
59860
  lines2.push("");
59797
59861
  }
59798
- lines2.push("Start the open challenge: chofex challenge");
59862
+ const hasOpenChallenge = catalog.challenges.some((challenge) => challenge.open && challenge.playable);
59863
+ if (hasOpenChallenge) {
59864
+ lines2.push("Empieza el challenge abierto: chofex challenge");
59865
+ } else {
59866
+ lines2.push("No hay un challenge abierto. Los rankings finales siguen visibles.");
59867
+ }
59799
59868
  return lines2.join(`
59800
59869
  `);
59801
59870
  };
@@ -59803,6 +59872,32 @@ var challengeShowText = (attempt) => {
59803
59872
  const { challenge, progress } = attempt;
59804
59873
  const queriesRemaining = progress.queriesLimit - progress.queriesUsed;
59805
59874
  const evaluationsRemaining = progress.evaluationsLimit - progress.evaluationsUsed;
59875
+ if (challenge.slug === "broken-agent") {
59876
+ let caseStatus2 = "READY TO AUDIT";
59877
+ if (attempt.latestEvaluation)
59878
+ caseStatus2 = "EVALUATED";
59879
+ const lines3 = [
59880
+ `${challenge.title.toUpperCase()} — CASE #${challenge.code}`,
59881
+ challenge.summary,
59882
+ "",
59883
+ `CASE STATUS: ${caseStatus2}`,
59884
+ "",
59885
+ "The public tests are green. Your job is to make the scheduler trustworthy under production conditions.",
59886
+ "AI tools are allowed.",
59887
+ "",
59888
+ `Evaluations ${evaluationsRemaining} / ${progress.evaluationsLimit} remaining`
59889
+ ];
59890
+ if (progress.bestAccuracy !== undefined) {
59891
+ lines3.push("", `Best score ${percent(progress.bestAccuracy)}`);
59892
+ if (progress.rank !== undefined)
59893
+ lines3.push(`Rank #${progress.rank}`);
59894
+ if (progress.shareCode)
59895
+ lines3.push(`Share code #${progress.shareCode}`);
59896
+ }
59897
+ lines3.push("", "YOUR NEXT MOVE", "Read the contract, audit scheduler.js, and keep the visible suite green.", " cd broken-agent && npm test", " chofex challenge test --challenge broken-agent --source ./broken-agent/scheduler.js");
59898
+ return lines3.join(`
59899
+ `);
59900
+ }
59806
59901
  let evidence = `${attempt.observations.length} observations`;
59807
59902
  if (attempt.observations.length === 1)
59808
59903
  evidence = "1 observation";
@@ -59811,6 +59906,8 @@ var challengeShowText = (attempt) => {
59811
59906
  caseStatus = "INVESTIGATING";
59812
59907
  if (attempt.latestEvaluation !== undefined)
59813
59908
  caseStatus = "EVALUATED";
59909
+ if (challenge.closed)
59910
+ caseStatus = "CERRADO";
59814
59911
  const lines2 = [
59815
59912
  `${challenge.title.toUpperCase()} — CASE #${challenge.code}`,
59816
59913
  challenge.summary,
@@ -59832,6 +59929,11 @@ var challengeShowText = (attempt) => {
59832
59929
  if (progress.shareCode)
59833
59930
  lines2.push(`Share code #${progress.shareCode}`);
59834
59931
  }
59932
+ if (challenge.closed) {
59933
+ lines2.push("", "SIGUIENTE PASO", "Revisa tu historial y el ranking final.", " chofex challenge notebook", " chofex challenge ranking");
59934
+ return lines2.join(`
59935
+ `);
59936
+ }
59835
59937
  lines2.push("", "YOUR NEXT MOVE");
59836
59938
  if (attempt.observations.length === 0) {
59837
59939
  lines2.push("Ask the machine to price one ordinary shipment.", " chofex challenge query");
@@ -59882,8 +59984,17 @@ var shipmentCells = (observation) => {
59882
59984
  express: String(record2.express ?? "")
59883
59985
  };
59884
59986
  };
59885
- var notebookTableText = (observations) => {
59987
+ var notebookTableText = (observations, challengeClosed = false) => {
59886
59988
  if (observations.length === 0) {
59989
+ if (challengeClosed) {
59990
+ return [
59991
+ "CASE FILE: no observations",
59992
+ "El challenge está cerrado. Revisa el ranking final.",
59993
+ "",
59994
+ "Siguiente: chofex challenge ranking"
59995
+ ].join(`
59996
+ `);
59997
+ }
59887
59998
  return [
59888
59999
  "CASE FILE: no observations yet",
59889
60000
  "The machine has not revealed anything. Start with one ordinary shipment.",
@@ -59917,6 +60028,11 @@ var notebookTableText = (observations) => {
59917
60028
  ].join("");
59918
60029
  });
59919
60030
  const lines2 = [`CASE FILE: ${observationLabel}`, "", header, ...rows, ""];
60031
+ if (challengeClosed) {
60032
+ lines2.push("El challenge está cerrado. Este cuaderno queda disponible como historial.", "Siguiente: chofex challenge ranking");
60033
+ return lines2.join(`
60034
+ `);
60035
+ }
59920
60036
  if (observations.length === 1) {
59921
60037
  lines2.push("One answer is a clue, not a rule.", "Next: run a controlled experiment. Change one field and keep the other four fixed.", " chofex challenge query", "");
59922
60038
  } else {
@@ -59944,6 +60060,26 @@ var notebookCsvText = (observations) => {
59944
60060
  `);
59945
60061
  };
59946
60062
  var challengeTestText = (result3) => {
60063
+ if (result3.kind === "broken_agent") {
60064
+ const lines3 = [
60065
+ "PUBLIC SUITE — BROKEN AGENT",
60066
+ `${result3.matchedObservations} / ${result3.observationCount} visible behaviors passing`
60067
+ ];
60068
+ if (result3.accuracy === 1) {
60069
+ lines3.push("", "Everything passes.", "Unfortunately, that does not mean the scheduler is production-correct.", "Reason about concurrency, restarts, failures, and idempotency before evaluating.", "", "Next: spend one official evaluation when you would ship it", " chofex challenge evaluate --challenge broken-agent --source ./broken-agent/scheduler.js");
60070
+ return lines3.join(`
60071
+ `);
60072
+ }
60073
+ if (result3.mismatches.length > 0) {
60074
+ lines3.push("", "Visible failures:");
60075
+ for (const mismatch of result3.mismatches) {
60076
+ lines3.push(` ${String(mismatch.actual)}`);
60077
+ }
60078
+ }
60079
+ lines3.push("", "Fix the public regressions before using an official evaluation.");
60080
+ return lines3.join(`
60081
+ `);
60082
+ }
59947
60083
  let verdict = "KEEP WORKING";
59948
60084
  if (result3.accuracy === 1)
59949
60085
  verdict = "NOTEBOOK MATCHED";
@@ -59970,6 +60106,34 @@ var challengeTestText = (result3) => {
59970
60106
  `);
59971
60107
  };
59972
60108
  var challengeEvaluateText = (result3) => {
60109
+ if (result3.breakdown) {
60110
+ const capabilityLines = [
60111
+ ["Core behavior", result3.breakdown.coreBehavior],
60112
+ ["Persistence", result3.breakdown.persistence],
60113
+ ["Concurrency", result3.breakdown.concurrency],
60114
+ ["Failure recovery", result3.breakdown.failureRecovery],
60115
+ ["Idempotency", result3.breakdown.idempotency],
60116
+ ["Regression safety", result3.breakdown.regressionSafety],
60117
+ ["Performance", result3.breakdown.performance]
60118
+ ];
60119
+ const lines3 = [
60120
+ "OFFICIAL VERDICT — BROKEN AGENT — PRODUCTION READINESS",
60121
+ `Score ${(result3.accuracy * 100).toFixed(2)} / ${result3.sampleSize}`,
60122
+ `Runtime ${result3.runtimeMs} ms`,
60123
+ "",
60124
+ "Capability scores",
60125
+ ...capabilityLines.map(([label, score]) => `${label.padEnd(20)}${score.earned.toFixed(2)} / ${score.available}`)
60126
+ ];
60127
+ if (result3.rank !== undefined) {
60128
+ lines3.push(`Rank #${result3.rank}`);
60129
+ }
60130
+ lines3.push(`Official evaluations remaining ${result3.evaluationsRemaining} / ${result3.evaluationsLimit}`, "", result3.shareText, "", "Next: inspect the leaderboard", " chofex challenge ranking --challenge broken-agent");
60131
+ if (result3.evaluationsRemaining > 0) {
60132
+ lines3.push("", "The evaluator reports capabilities, not individual hidden cases. Reason before submitting again.", " chofex challenge test --challenge broken-agent --source ./broken-agent/scheduler.js");
60133
+ }
60134
+ return lines3.join(`
60135
+ `);
60136
+ }
59973
60137
  const lines2 = [
59974
60138
  "OFFICIAL VERDICT — BLACK BOX REPLICATION",
59975
60139
  `Accuracy ${percent(result3.accuracy)}`,
@@ -59998,6 +60162,18 @@ var challengeRankingText = (ranking, now3 = new Date) => {
59998
60162
  if (ranking.entries.length === 0) {
59999
60163
  lines2.push("No official evaluations yet.");
60000
60164
  return lines2.join(`
60165
+ `);
60166
+ }
60167
+ if (ranking.challenge.slug === "broken-agent") {
60168
+ lines2.push("Rank Score Points Runtime Name");
60169
+ for (const entry of ranking.entries.slice(0, 20)) {
60170
+ const rank = String(entry.rank).padStart(4, " ");
60171
+ const accuracy = percent(entry.accuracy).padStart(8, " ");
60172
+ const points = `${entry.exactCount}/${entry.sampleSize}`.padStart(11, " ");
60173
+ const runtime = `${entry.runtimeMs}ms`.padStart(8, " ");
60174
+ lines2.push(`${rank} ${accuracy} ${points} ${runtime} ${entry.displayName} #${entry.shareCode}`);
60175
+ }
60176
+ return lines2.join(`
60001
60177
  `);
60002
60178
  }
60003
60179
  lines2.push("Rank Accuracy Exact Queries Name");
@@ -60034,7 +60210,216 @@ var registrationPartsText = (result3) => {
60034
60210
  };
60035
60211
 
60036
60212
  // src/challenge-scaffold.ts
60037
- import { writeFile as writeFile3 } from "node:fs/promises";
60213
+ import { mkdir as mkdir2, writeFile as writeFile3 } from "node:fs/promises";
60214
+
60215
+ // ../../packages/challenges-contract/src/broken-agent.ts
60216
+ var brokenAgentScaffoldDirectory = "broken-agent";
60217
+ var brokenAgentSolutionPath = `${brokenAgentScaffoldDirectory}/scheduler.js`;
60218
+ var brokenAgentPackageJson = `${JSON.stringify({
60219
+ name: "broken-agent-challenge",
60220
+ private: true,
60221
+ type: "commonjs",
60222
+ scripts: { test: "node --test scheduler.test.js" }
60223
+ }, null, 2)}
60224
+ `;
60225
+ var brokenAgentStarterSource = `/**
60226
+ * Broken Agent — The Scheduler
60227
+ *
60228
+ * A coding agent implemented this scheduler and declared the feature complete.
60229
+ * Every public test passes. The implementation is not production-correct.
60230
+ * Keep the createScheduler interface intact and harden the implementation.
60231
+ */
60232
+ function createScheduler({ store, clock, execute }) {
60233
+ return {
60234
+ async schedule(input) {
60235
+ const job = { ...input, status: "pending" };
60236
+ await store.transaction((tx) => tx.put(job));
60237
+ return job;
60238
+ },
60239
+
60240
+ async cancel(id) {
60241
+ return store.transaction((tx) => {
60242
+ const job = tx.get(id);
60243
+ if (!job || job.status !== "pending") return false;
60244
+ tx.put({ ...job, status: "cancelled" });
60245
+ return true;
60246
+ });
60247
+ },
60248
+
60249
+ async list() {
60250
+ return store.transaction((tx) => tx.list());
60251
+ },
60252
+
60253
+ async runDue() {
60254
+ const now = clock.now().getTime();
60255
+ const jobs = await store.transaction((tx) => tx.list());
60256
+ for (const job of jobs) {
60257
+ if (job.status !== "pending" || Date.parse(job.runAt) > now) continue;
60258
+ await execute(job);
60259
+ await store.transaction((tx) =>
60260
+ tx.put({ ...job, status: "completed" }),
60261
+ );
60262
+ }
60263
+ },
60264
+ };
60265
+ }
60266
+
60267
+ module.exports = { createScheduler };
60268
+ `;
60269
+ var brokenAgentReadme = `# Broken Agent — The Scheduler
60270
+
60271
+ > A coding agent was asked to implement a feature. It says it is done. It is
60272
+ > not. Your job is to ship it.
60273
+
60274
+ The repository starts with all public tests passing. Repair \`scheduler.js\`
60275
+ without changing its exported \`createScheduler(dependencies)\` interface.
60276
+
60277
+ ## Commands
60278
+
60279
+ \`\`\`sh
60280
+ npm test
60281
+ chofex challenge test --challenge broken-agent --source ./broken-agent/scheduler.js
60282
+ chofex challenge evaluate --challenge broken-agent --source ./broken-agent/scheduler.js
60283
+ \`\`\`
60284
+
60285
+ Local and public tests are unlimited. You have **5 official evaluations**
60286
+ against hidden production scenarios. AI tools are explicitly allowed.
60287
+
60288
+ ## Public contract
60289
+
60290
+ \`createScheduler({ store, clock, execute, workerId })\` returns:
60291
+
60292
+ - \`schedule({ id, runAt, payload })\`
60293
+ - \`cancel(id)\`
60294
+ - \`list()\`
60295
+ - \`runDue()\`
60296
+
60297
+ ### Jobs
60298
+
60299
+ - \`id\` matches \`^[A-Za-z0-9_-]{1,64}$\`.
60300
+ - \`runAt\` is an ISO-8601 timestamp with an explicit timezone.
60301
+ - \`payload\` is JSON-serializable.
60302
+ - Scheduling an identical job ID is idempotent. Reusing an ID for different
60303
+ work must reject without changing the original job.
60304
+ - Past timestamps are valid and become due immediately.
60305
+ - \`list()\` returns jobs ordered by \`runAt\`, then \`id\`.
60306
+
60307
+ ### Durability and concurrency
60308
+
60309
+ - The supplied store survives scheduler instances and process restarts.
60310
+ - Several workers may call \`runDue()\` concurrently against the same store.
60311
+ - A worker claim lasts 30 seconds. Another worker may reclaim the job when
60312
+ \`clock.now()\` reaches that lease deadline.
60313
+ - A pending job may be cancelled. A running or terminal job may not.
60314
+ - Failed executions retry, without preventing other due jobs from running.
60315
+ - A job becomes \`failed\` after 3 unsuccessful attempts.
60316
+ - The execution adapter applies effects idempotently by job ID. A worker may
60317
+ stop after the effect is applied but before completion is recorded; retrying
60318
+ must eventually complete the job without duplicating the effect.
60319
+
60320
+ The store exposes only \`transaction(action)\`. Inside an action, \`tx\`
60321
+ provides synchronous \`get(id)\`, \`put(job)\`, \`delete(id)\`, and \`list()\`
60322
+ operations. Transactions are serialized. Do not keep a transaction open while
60323
+ executing a job.
60324
+
60325
+ \`clock.now()\` returns a \`Date\`. \`execute(job)\` returns a promise.
60326
+
60327
+ ## Evaluation
60328
+
60329
+ The hidden evaluator checks the published contract under deterministic
60330
+ concurrency, restart, failure, idempotency, compatibility, and load scenarios.
60331
+ It reports capability-level scores, not individual hidden-test failures.
60332
+
60333
+ - Core behavior: 10 points
60334
+ - Persistence: 15 points
60335
+ - Concurrency: 20 points
60336
+ - Failure recovery: 20 points
60337
+ - Idempotency: 15 points
60338
+ - Regression safety: 15 points
60339
+ - Performance: 5 points, available only when the load jobs complete correctly
60340
+
60341
+ Ranking order is total score, fewer official evaluations used, runtime, then
60342
+ official submission time. Submission time is only the final tie-breaker.
60343
+ `;
60344
+ var brokenAgentPublicTestSource = `const test = require("node:test");
60345
+ const assert = require("node:assert/strict");
60346
+ const { createScheduler } = require("./scheduler.js");
60347
+
60348
+ const clone = (value) => {
60349
+ if (value === undefined) return undefined;
60350
+ return JSON.parse(JSON.stringify(value));
60351
+ };
60352
+
60353
+ class MemoryStore {
60354
+ constructor() {
60355
+ this.jobs = new Map();
60356
+ this.tail = Promise.resolve();
60357
+ }
60358
+
60359
+ transaction(action) {
60360
+ const run = this.tail.then(() => action({
60361
+ get: (id) => clone(this.jobs.get(id)),
60362
+ put: (job) => {
60363
+ this.jobs.set(job.id, clone(job));
60364
+ return clone(job);
60365
+ },
60366
+ delete: (id) => this.jobs.delete(id),
60367
+ list: () => [...this.jobs.values()].map(clone),
60368
+ }));
60369
+ this.tail = run.then(() => undefined, () => undefined);
60370
+ return run;
60371
+ }
60372
+ }
60373
+
60374
+ const setup = () => {
60375
+ const store = new MemoryStore();
60376
+ const now = new Date("2026-10-17T15:00:00.000Z");
60377
+ const executed = [];
60378
+ const scheduler = createScheduler({
60379
+ store,
60380
+ clock: { now: () => new Date(now) },
60381
+ execute: async (job) => executed.push(job.id),
60382
+ workerId: "public-worker",
60383
+ });
60384
+ return { store, scheduler, executed };
60385
+ };
60386
+
60387
+ const job = (id, runAt = "2026-10-17T15:00:00.000Z") => ({
60388
+ id,
60389
+ runAt,
60390
+ payload: { message: id },
60391
+ });
60392
+
60393
+ test("schedules and lists a job", async () => {
60394
+ const { scheduler } = setup();
60395
+ await scheduler.schedule(job("job-1"));
60396
+ assert.equal((await scheduler.list()).length, 1);
60397
+ });
60398
+
60399
+ test("executes a due job", async () => {
60400
+ const { scheduler, executed } = setup();
60401
+ await scheduler.schedule(job("job-1"));
60402
+ await scheduler.runDue();
60403
+ assert.deepEqual(executed, ["job-1"]);
60404
+ });
60405
+
60406
+ test("does not execute a future job", async () => {
60407
+ const { scheduler, executed } = setup();
60408
+ await scheduler.schedule(job("future", "2026-10-17T16:00:00.000Z"));
60409
+ await scheduler.runDue();
60410
+ assert.deepEqual(executed, []);
60411
+ });
60412
+
60413
+ test("cancels a pending job", async () => {
60414
+ const { scheduler, executed } = setup();
60415
+ await scheduler.schedule(job("cancel-me"));
60416
+ assert.equal(await scheduler.cancel("cancel-me"), true);
60417
+ await scheduler.runDue();
60418
+ assert.deepEqual(executed, []);
60419
+ });
60420
+ `;
60421
+
60422
+ // src/challenge-scaffold.ts
60038
60423
  var solutionPath = "shipping.js";
60039
60424
  var starterSource = `/**
60040
60425
  * The Shipping Machine
@@ -60055,7 +60440,36 @@ function calculateShipping(input) {
60055
60440
  return 0;
60056
60441
  }
60057
60442
  `;
60058
- var createChallengeScaffold = exports_Effect.fn("createChallengeScaffold")(function* () {
60443
+ var createChallengeScaffold = exports_Effect.fn("createChallengeScaffold")(function* (challengeSlug = "black-box") {
60444
+ const challenge = challengeBySlug(challengeSlug);
60445
+ if (!challenge?.playable) {
60446
+ return yield* exports_Effect.fail(cliError("CHALLENGE_NOT_AVAILABLE", `Challenge ${challengeSlug} is not available`));
60447
+ }
60448
+ if (challengeSlug === "broken-agent") {
60449
+ const status2 = yield* exports_Effect.tryPromise({
60450
+ try: async () => {
60451
+ await mkdir2(brokenAgentScaffoldDirectory);
60452
+ await Promise.all([
60453
+ writeFile3(brokenAgentSolutionPath, brokenAgentStarterSource, "utf8"),
60454
+ writeFile3(`${brokenAgentScaffoldDirectory}/scheduler.test.js`, brokenAgentPublicTestSource, "utf8"),
60455
+ writeFile3(`${brokenAgentScaffoldDirectory}/README.md`, brokenAgentReadme, "utf8"),
60456
+ writeFile3(`${brokenAgentScaffoldDirectory}/package.json`, brokenAgentPackageJson, "utf8")
60457
+ ]);
60458
+ return "created";
60459
+ },
60460
+ catch: (error2) => error2
60461
+ }).pipe(exports_Effect.catch((error2) => {
60462
+ if (exports_Predicate.hasProperty(error2, "code") && error2.code === "EEXIST") {
60463
+ return exports_Effect.succeed("exists");
60464
+ }
60465
+ return exports_Effect.fail(cliError("SCAFFOLD_FAILED", `Could not create ${brokenAgentScaffoldDirectory}: ${String(error2)}`));
60466
+ }));
60467
+ return {
60468
+ challenge: challengeSlug,
60469
+ path: brokenAgentScaffoldDirectory,
60470
+ status: status2
60471
+ };
60472
+ }
60059
60473
  const status = yield* exports_Effect.tryPromise({
60060
60474
  try: async () => {
60061
60475
  await writeFile3(solutionPath, starterSource, {
@@ -60071,7 +60485,7 @@ var createChallengeScaffold = exports_Effect.fn("createChallengeScaffold")(funct
60071
60485
  }
60072
60486
  return exports_Effect.fail(cliError("SCAFFOLD_FAILED", `Could not create ${solutionPath}: ${String(error2)}`));
60073
60487
  }));
60074
- return { path: solutionPath, status };
60488
+ return { challenge: challengeSlug, path: solutionPath, status };
60075
60489
  });
60076
60490
 
60077
60491
  // src/cli-root.ts
@@ -60273,12 +60687,33 @@ var launchNoticeFor = (slug) => {
60273
60687
  const challenge = challengeBySlug(slug);
60274
60688
  if (!challenge)
60275
60689
  return;
60276
- return challengeLaunchNotice(challenge.theme, challenge.opensAt);
60690
+ return challengeParticipationNotice(challenge.theme, challenge.opensAt, challenge.closesAt);
60277
60691
  };
60278
- var challengeQuickstartText = (launchNotice) => {
60692
+ var participationStateFor = (slug) => {
60693
+ const challenge = challengeBySlug(slug);
60694
+ if (!challenge)
60695
+ return "scheduled";
60696
+ const now3 = new Date;
60697
+ if (isChallengeClosedAt(challenge, now3))
60698
+ return "closed";
60699
+ if (isChallengeOpenAt(challenge, now3))
60700
+ return "open";
60701
+ return "scheduled";
60702
+ };
60703
+ var challengeQuickstartText = (participationState, launchNotice) => {
60279
60704
  const lines2 = [challengeQuickstart.title];
60280
60705
  if (launchNotice)
60281
60706
  lines2.push("", "LAUNCH NOTICE", launchNotice);
60707
+ if (participationState === "closed") {
60708
+ lines2.push("", "RANKING FINAL", " chofex challenge ranking");
60709
+ return lines2.join(`
60710
+ `);
60711
+ }
60712
+ if (participationState === "scheduled") {
60713
+ lines2.push("", "PRÓXIMAMENTE", " chofex challenge list");
60714
+ return lines2.join(`
60715
+ `);
60716
+ }
60282
60717
  lines2.push("", ...challengeQuickstart.story, "", "YOUR MISSION", challengeQuickstart.mission, "", "RULES OF THE GAME", ...challengeQuickstart.rules.map((rule) => `• ${rule}`), "", "FIELD GUIDE", "");
60283
60718
  for (const item of challengeQuickstart.workflow) {
60284
60719
  lines2.push(`${item.step}. ${item.action}`);
@@ -60291,7 +60726,7 @@ var challengeQuickstartText = (launchNotice) => {
60291
60726
  };
60292
60727
  var optionalString = (name, description) => exports_Flag.string(name).pipe(exports_Flag.optional, exports_Flag.withDescription(description));
60293
60728
  var challengeFlag = exports_Flag.string("challenge").pipe(exports_Flag.withDefault(defaultChallengeSlug), exports_Flag.withDescription("Challenge slug (default: black-box)"));
60294
- var sourceFlag = optionalString("source", "JavaScript file defining function calculateShipping(input)");
60729
+ var sourceFlag = optionalString("source", "JavaScript challenge solution file");
60295
60730
  var inputFlag = optionalString("input", "JSON file, or - for stdin");
60296
60731
  var formatFlag = exports_Flag.choice("format", ["table", "json", "csv"]).pipe(exports_Flag.withDefault("table"), exports_Flag.withDescription("Notebook format"));
60297
60732
  var numberFromOption = (value3) => {
@@ -60308,6 +60743,30 @@ var booleanFromOption = (value3) => {
60308
60743
  return false;
60309
60744
  };
60310
60745
  var challengeInitText = (result3) => {
60746
+ if (result3.challenge === "broken-agent") {
60747
+ if (result3.status === "exists") {
60748
+ return [
60749
+ `${result3.path} already exists. Left it unchanged.`,
60750
+ "",
60751
+ "Next: keep hardening your scheduler, then run the public tests",
60752
+ ` cd ${result3.path} && npm test`,
60753
+ ` chofex challenge test --challenge broken-agent --source ./${result3.path}/scheduler.js`
60754
+ ].join(`
60755
+ `);
60756
+ }
60757
+ return [
60758
+ `Created ${result3.path}`,
60759
+ "The previous agent says the scheduler is done. Everything passes.",
60760
+ "Unfortunately, the agent was wrong.",
60761
+ "",
60762
+ "Start with the public contract and tests:",
60763
+ ` cd ${result3.path} && npm test`,
60764
+ "",
60765
+ "Then repair scheduler.js and test it safely:",
60766
+ ` chofex challenge test --challenge broken-agent --source ./${result3.path}/scheduler.js`
60767
+ ].join(`
60768
+ `);
60769
+ }
60311
60770
  if (result3.status === "exists") {
60312
60771
  return [
60313
60772
  `${result3.path} already exists. Left it unchanged.`,
@@ -60339,19 +60798,32 @@ var listCommand = exports_Command.make("list", {}, exports_Effect.fn("challengeL
60339
60798
  description: "See which challenge is currently playable"
60340
60799
  }
60341
60800
  ]));
60342
- var initCommand = exports_Command.make("init", {}, exports_Effect.fn("challengeInitCommand")(function* () {
60801
+ var initCommand = exports_Command.make("init", { challenge: challengeFlag }, exports_Effect.fn("challengeInitCommand")(function* ({ challenge }) {
60343
60802
  const options = yield* root;
60344
- const operation = createChallengeScaffold().pipe(exports_Effect.map((data) => ({
60803
+ const participationState = participationStateFor(challenge);
60804
+ if (participationState !== "open") {
60805
+ const message = launchNoticeFor(challenge) ?? "El challenge todavía no está disponible.";
60806
+ let code = "CHALLENGE_NOT_OPEN";
60807
+ if (participationState === "closed")
60808
+ code = "CHALLENGE_CLOSED";
60809
+ yield* execute(options.output, exports_Effect.fail(cliError(code, message)), challengeInitText);
60810
+ return;
60811
+ }
60812
+ const operation = createChallengeScaffold(challenge).pipe(exports_Effect.map((data) => ({
60345
60813
  version: 1,
60346
60814
  ok: true,
60347
60815
  requestId: crypto.randomUUID(),
60348
60816
  data
60349
60817
  })));
60350
60818
  yield* execute(options.output, operation, challengeInitText);
60351
- })).pipe(exports_Command.withDescription("Create a documented shipping.js starter in the current directory. Existing files are never overwritten."), exports_Command.withExamples([
60819
+ })).pipe(exports_Command.withDescription("Create a challenge starter without overwriting existing work."), exports_Command.withExamples([
60352
60820
  {
60353
60821
  command: "chofex challenge init",
60354
60822
  description: "Prepare a safe JavaScript solution file"
60823
+ },
60824
+ {
60825
+ command: "chofex challenge init --challenge broken-agent",
60826
+ description: "Prepare the Broken Agent starter repository"
60355
60827
  }
60356
60828
  ]));
60357
60829
  var showCommand = exports_Command.make("show", { challenge: challengeFlag }, exports_Effect.fn("challengeShowCommand")(function* ({ challenge }) {
@@ -60414,7 +60886,7 @@ var notebookCommand = exports_Command.make("notebook", { challenge: challengeFla
60414
60886
  if (format6 === "json") {
60415
60887
  return JSON.stringify(attempt.observations, null, 2);
60416
60888
  }
60417
- return notebookTableText(attempt.observations);
60889
+ return notebookTableText(attempt.observations, Boolean(attempt.challenge.closed));
60418
60890
  });
60419
60891
  })).pipe(exports_Command.withDescription("Review every input and price you observed. Export table, JSON, or CSV for analysis. Requires sign-in."), exports_Command.withExamples([
60420
60892
  {
@@ -60430,11 +60902,11 @@ var testCommand = exports_Command.make("test", { challenge: challengeFlag, sourc
60430
60902
  const options = yield* root;
60431
60903
  const token = exports_Option.getOrUndefined(options.token);
60432
60904
  const operation = exports_Effect.gen(function* () {
60433
- const solution = yield* javascriptSourceFromPath(exports_Option.getOrUndefined(source));
60905
+ const solution = yield* javascriptSourceFromPath(exports_Option.getOrUndefined(source), challenge);
60434
60906
  return yield* testChallenge({ apiUrl: options.apiUrl, token }, challenge, solution);
60435
60907
  });
60436
60908
  yield* execute(options.output, operation, challengeTestText);
60437
- })).pipe(exports_Command.withDescription("Check calculateShipping against your saved observations. Safe to repeat; official evaluations are not consumed. Requires sign-in."), exports_Command.withExamples([
60909
+ })).pipe(exports_Command.withDescription("Run the challenge's public tests. Safe to repeat; official evaluations are not consumed. Requires sign-in."), exports_Command.withExamples([
60438
60910
  {
60439
60911
  command: "chofex challenge test --source ./shipping.js",
60440
60912
  description: "Test a JavaScript replacement against your notebook"
@@ -60444,11 +60916,11 @@ var evaluateCommand = exports_Command.make("evaluate", { challenge: challengeFla
60444
60916
  const options = yield* root;
60445
60917
  const token = exports_Option.getOrUndefined(options.token);
60446
60918
  const operation = exports_Effect.gen(function* () {
60447
- const solution = yield* javascriptSourceFromPath(exports_Option.getOrUndefined(source));
60919
+ const solution = yield* javascriptSourceFromPath(exports_Option.getOrUndefined(source), challenge);
60448
60920
  return yield* evaluateChallenge({ apiUrl: options.apiUrl, token }, challenge, solution);
60449
60921
  });
60450
60922
  yield* execute(options.output, operation, challengeEvaluateText);
60451
- })).pipe(exports_Command.withDescription("Score calculateShipping on hidden cases. This consumes one limited official evaluation. Requires sign-in."), exports_Command.withExamples([
60923
+ })).pipe(exports_Command.withDescription("Score a solution on hidden cases. This consumes one limited official evaluation. Requires sign-in."), exports_Command.withExamples([
60452
60924
  {
60453
60925
  command: "chofex challenge evaluate --source ./shipping.js",
60454
60926
  description: "Spend one official evaluation when your solution is ready"
@@ -60465,10 +60937,38 @@ var rankingCommand = exports_Command.make("ranking", { challenge: challengeFlag
60465
60937
  ]));
60466
60938
  var challengeCommand = exports_Command.make("challenge", {}, exports_Effect.fn("challengeQuickstartCommand")(function* () {
60467
60939
  const options = yield* root;
60940
+ const participationState = participationStateFor(defaultChallengeSlug);
60941
+ const participationOpen = participationState === "open";
60468
60942
  const launchNotice = launchNoticeFor(defaultChallengeSlug);
60469
60943
  if (options.output === "json") {
60944
+ let workflow = challengeQuickstart.workflow;
60945
+ let helpCommand = challengeQuickstart.helpCommand;
60946
+ let story = challengeQuickstart.story;
60947
+ let mission = challengeQuickstart.mission;
60948
+ let rules = challengeQuickstart.rules;
60949
+ if (participationState === "closed") {
60950
+ const rankingStep = challengeQuickstart.workflow.at(-1);
60951
+ workflow = rankingStep ? [rankingStep] : [];
60952
+ helpCommand = "chofex challenge ranking --help";
60953
+ story = [];
60954
+ mission = "El Challenge 1 terminó. El ranking final sigue disponible.";
60955
+ rules = [];
60956
+ } else if (participationState === "scheduled") {
60957
+ workflow = [];
60958
+ helpCommand = "chofex challenge list --help";
60959
+ story = [];
60960
+ mission = "El challenge todavía no está disponible.";
60961
+ rules = [];
60962
+ }
60470
60963
  const data = {
60471
- ...challengeQuickstart
60964
+ ...challengeQuickstart,
60965
+ story,
60966
+ mission,
60967
+ rules,
60968
+ workflow,
60969
+ helpCommand,
60970
+ open: participationOpen,
60971
+ state: participationState
60472
60972
  };
60473
60973
  if (launchNotice)
60474
60974
  data.notice = launchNotice;
@@ -60480,7 +60980,7 @@ var challengeCommand = exports_Command.make("challenge", {}, exports_Effect.fn("
60480
60980
  });
60481
60981
  return;
60482
60982
  }
60483
- yield* exports_Console.log(challengeQuickstartText(launchNotice));
60983
+ yield* exports_Console.log(challengeQuickstartText(participationState, launchNotice));
60484
60984
  })).pipe(exports_Command.withDescription("Play mini technical challenges. Start here: learn the Black Box workflow from first query to leaderboard. Run without a subcommand for the guided quickstart."), exports_Command.withExamples([
60485
60985
  {
60486
60986
  command: "chofex challenge",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chofex-cli",
3
- "version": "0.1.172",
3
+ "version": "0.1.177",
4
4
  "description": "Command-line client for Hack the Andes",
5
5
  "type": "module",
6
6
  "bin": {