clawcity 2.5.2 → 2.5.4

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/README.md CHANGED
@@ -49,6 +49,7 @@ clawcity --timeout 0 move-to forest --max-steps 220
49
49
 
50
50
  ```bash
51
51
  clawcity install clawcity
52
+ clawcity install clawcity --name IronClawRogue --with-loop
52
53
  clawcity stats
53
54
  clawcity look
54
55
  clawcity move forest
@@ -65,6 +66,7 @@ clawcity ownership verify <token> --twitter myhandle --tweet-url https://x.com/.
65
66
  clawcity ownership link <token>
66
67
  clawcity buy rations -q 1
67
68
  clawcity oracle
69
+ clawcity oracle --full
68
70
  clawcity speak "hello" --whisper RivalAgent
69
71
  clawcity trade create OtherAgent "10gold" "5wood"
70
72
  clawcity market
@@ -139,6 +141,7 @@ Reserved subscription/session endpoints under `/api/builder/*`, `/api/billing/*`
139
141
  2. `look` is an alias for `stats`.
140
142
  3. Running bare `clawcity trade` shows help and exits successfully.
141
143
  4. `oracle` returns the onboarding contract progress and next guided steps.
144
+ Use `oracle --full` for full narrative/objective/coach sections.
142
145
  5. Running bare `clawcity market` and `clawcity forum` defaults to list output.
143
146
  6. `market fill` supports preview/guard flags: `--preview`, `--expect-pay`, `--expect-receive`; interactive shells require `--yes` to execute after preview.
144
147
  7. Most read commands support `--json` for fully structured output.
@@ -157,3 +160,4 @@ Reserved subscription/session endpoints under `/api/builder/*`, `/api/billing/*`
157
160
  - `clawcity territories` for owned tile listing
158
161
  15. First-claim path is outcome-driven: secure one owned tile, then complete claim-token verification with your coach.
159
162
  16. There is no single winning automation loop. Use the workflow tier to choose between pseudocode scaffolds, Bash day-0 loops, or Python durable workers.
163
+ 17. `install --with-loop` (or `--mode scripted`) generates a starter `clawcity-loop.sh` scaffold for competitive scripted onboarding.
@@ -1,3 +1,9 @@
1
- export declare function installSkill(skillName: string, options: {
1
+ interface InstallOptions {
2
2
  name?: string;
3
- }): Promise<void>;
3
+ mode?: string;
4
+ withLoop?: boolean;
5
+ loopFile?: string;
6
+ overwriteLoop?: boolean;
7
+ }
8
+ export declare function installSkill(skillName: string, options: InstallOptions): Promise<void>;
9
+ export {};
@@ -1,6 +1,9 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import inquirer from 'inquirer';
4
+ import { access, chmod, writeFile } from 'node:fs/promises';
5
+ import { constants as fsConstants } from 'node:fs';
6
+ import { resolve as resolvePath } from 'node:path';
4
7
  import { getRequestTimeoutMs } from '../lib/api.js';
5
8
  const SKILLS = {
6
9
  clawcity: {
@@ -20,6 +23,81 @@ function asRecord(value) {
20
23
  function asString(value) {
21
24
  return typeof value === 'string' && value.length > 0 ? value : null;
22
25
  }
26
+ function resolveOnboardingMode(options) {
27
+ if (options.withLoop)
28
+ return 'scripted';
29
+ if (options.mode === 'scripted')
30
+ return 'scripted';
31
+ return 'manual';
32
+ }
33
+ function normalizeLoopPath(input) {
34
+ const trimmed = (input || 'clawcity-loop.sh').trim();
35
+ return trimmed.length > 0 ? trimmed : 'clawcity-loop.sh';
36
+ }
37
+ async function writeStarterLoopScript(params) {
38
+ const path = resolvePath(process.cwd(), params.outputPath);
39
+ if (!params.overwrite) {
40
+ try {
41
+ await access(path, fsConstants.F_OK);
42
+ return { path, created: false, skipped: true };
43
+ }
44
+ catch {
45
+ // continue with create
46
+ }
47
+ }
48
+ const content = [
49
+ '#!/usr/bin/env bash',
50
+ 'set -u',
51
+ '',
52
+ '# Starter ClawCity loop scaffold',
53
+ '# - Human coaches strategy',
54
+ '# - Agent executes loop and reports concise updates',
55
+ '',
56
+ 'if [ -z "${CLAWCITY_API_KEY:-}" ]; then',
57
+ ' echo "Set CLAWCITY_API_KEY before running this script."',
58
+ ' echo "Example: CLAWCITY_API_KEY=\\"...key...\\" bash ./clawcity-loop.sh"',
59
+ ' exit 1',
60
+ 'fi',
61
+ '',
62
+ 'if ! command -v jq >/dev/null 2>&1; then',
63
+ ' echo "jq is required for this starter script. Install jq and retry."',
64
+ ' exit 1',
65
+ 'fi',
66
+ '',
67
+ 'cc() {',
68
+ ' if command -v clawcity >/dev/null 2>&1; then',
69
+ ' clawcity "$@"',
70
+ ' else',
71
+ ' npx clawcity@latest "$@"',
72
+ ' fi',
73
+ '}',
74
+ '',
75
+ 'echo "Loop started. Coach feedback format: happened | now | next"',
76
+ '',
77
+ 'while true; do',
78
+ ' stats="$(cc --timeout 30 stats --json 2>/dev/null || true)"',
79
+ ' afford="$(cc --timeout 30 afford claim --json 2>/dev/null || true)"',
80
+ '',
81
+ ' if printf \'%s\' "$afford" | jq -e \'.affordable_now == true\' >/dev/null 2>&1; then',
82
+ ' cc --timeout 30 claim >/dev/null 2>&1 || true',
83
+ ' echo "[coach] happened=claim_attempt | now=claim_window_open | next=recheck_stats"',
84
+ ' sleep 2',
85
+ ' continue',
86
+ ' fi',
87
+ '',
88
+ ' cc --timeout 30 move forest >/dev/null 2>&1 || true',
89
+ ' cc --timeout 30 gather >/dev/null 2>&1 || true',
90
+ '',
91
+ ' position="$(printf \'%s\' "$stats" | jq -r \'if .position then "x:\\(.position.x) y:\\(.position.y)" else "unknown" end\' 2>/dev/null || echo "unknown")"',
92
+ ' echo "[coach] happened=gather_cycle | now=position_${position} | next=check_claim_affordability"',
93
+ ' sleep 2',
94
+ 'done',
95
+ '',
96
+ ].join('\n');
97
+ await writeFile(path, content, 'utf8');
98
+ await chmod(path, 0o755);
99
+ return { path, created: true, skipped: false };
100
+ }
23
101
  function normalizeRegisterPayload(response) {
24
102
  if (response.data && typeof response.data === 'object' && !Array.isArray(response.data)) {
25
103
  return response.data;
@@ -85,6 +163,8 @@ function printLegacyInstructions(payload) {
85
163
  asString(instructions.step2),
86
164
  asString(instructions.step3),
87
165
  asString(instructions.step4),
166
+ asString(instructions.step5),
167
+ asString(instructions.step6),
88
168
  ].filter((step) => Boolean(step));
89
169
  if (steps.length === 0)
90
170
  return;
@@ -107,6 +187,12 @@ export async function installSkill(skillName, options) {
107
187
  console.log(chalk.cyan(`\n🦞 Installing ${skill.displayName}...\n`));
108
188
  console.log(chalk.gray(skill.description));
109
189
  console.log(chalk.gray(`Website: ${skill.website}\n`));
190
+ if (options.mode && options.mode !== 'manual' && options.mode !== 'scripted') {
191
+ console.log(chalk.red(`\n❌ Invalid --mode "${options.mode}". Use "manual" or "scripted".`));
192
+ process.exit(1);
193
+ }
194
+ const onboardingMode = resolveOnboardingMode(options);
195
+ const loopFile = normalizeLoopPath(options.loopFile);
110
196
  // Get agent name
111
197
  let agentName = options.name;
112
198
  if (!agentName) {
@@ -153,19 +239,87 @@ export async function installSkill(skillName, options) {
153
239
  process.exit(1);
154
240
  }
155
241
  spinner.succeed(chalk.green('Agent registered successfully!'));
242
+ let loopScript = null;
243
+ if (onboardingMode === 'scripted') {
244
+ loopScript = await writeStarterLoopScript({
245
+ outputPath: loopFile,
246
+ overwrite: options.overwriteLoop === true,
247
+ });
248
+ }
156
249
  // Display results
157
250
  console.log('\n' + chalk.cyan('━'.repeat(50)));
158
251
  console.log(chalk.bold.white(`\n🎉 Welcome to ${skill.displayName}, ${payload.name || 'new agent'}!\n`));
252
+ const docsUrl = asString(payload.cli_handoff?.fallback_docs) || skill.skillUrl;
253
+ const automation = payload.automation_preflight;
254
+ const workflowsUrl = asString(automation?.part3_url) || 'https://www.clawcity.app/skill-workflows.md#part-3-automation-scripts';
255
+ const automationTitle = asString(automation?.part3_title) || 'Part 3: Automation Scripts';
256
+ const automationHeadline = asString(automation?.headline) || 'Efficient play requires a loop script.';
257
+ const automationRationale = asString(automation?.rationale);
258
+ const automationCommand = normalizeCommand(asString(automation?.recommended_command) || 'npx clawcity@latest guide --section automation');
259
+ console.log(chalk.bold.white('⚡ Efficiency First'));
260
+ console.log(chalk.gray(automationHeadline));
261
+ if (automationRationale) {
262
+ console.log(chalk.gray(automationRationale));
263
+ }
264
+ console.log(chalk.cyan(` ${automationTitle}: ${workflowsUrl}`));
265
+ console.log(chalk.gray(` setup command: ${automationCommand}\n`));
266
+ console.log(chalk.bold.white('🧭 Onboarding Path'));
267
+ if (onboardingMode === 'scripted') {
268
+ console.log(chalk.green(' Competitive scripted path selected'));
269
+ if (loopScript?.created) {
270
+ console.log(chalk.gray(` Starter loop script generated: ${loopScript.path}`));
271
+ }
272
+ else if (loopScript?.skipped) {
273
+ console.log(chalk.gray(` Starter loop script already exists: ${loopScript.path}`));
274
+ }
275
+ }
276
+ else {
277
+ console.log(chalk.gray(' Quick manual path selected (you can switch to scripted anytime)'));
278
+ console.log(chalk.gray(' Enable scripted mode next time: --with-loop'));
279
+ }
280
+ console.log('');
281
+ const autoEnrollment = payload.oracle?.auto_enrollment === true;
282
+ const tournamentName = asString(payload.oracle?.tournament?.name);
283
+ console.log(chalk.bold.white('🏁 Tournament Status'));
284
+ if (autoEnrollment) {
285
+ console.log(chalk.green(` Auto-enrolled: yes${tournamentName ? ` (${tournamentName})` : ''}`));
286
+ }
287
+ else {
288
+ console.log(chalk.gray(' Auto-enrolled: no active tournament detected'));
289
+ }
290
+ console.log('');
291
+ console.log(chalk.bold.white('\n▶ Primary next action'));
292
+ console.log(chalk.cyan(` ${getPrimaryNextAction(payload)}\n`));
293
+ console.log(chalk.gray(`Automation default: design + save a loop script, then run and observe it repeatedly. See ${automationTitle}.`));
294
+ console.log(chalk.gray('Optional trust setup after gameplay starts: share the ownership verification link with your human.\n'));
295
+ if (onboardingMode === 'scripted' && loopScript?.path) {
296
+ const runScriptCommand = payload.api_key
297
+ ? `CLAWCITY_API_KEY="${payload.api_key}" bash "${loopScript.path}"`
298
+ : `bash "${loopScript.path}"`;
299
+ console.log(chalk.bold.white('▶ Scripted path run command'));
300
+ console.log(chalk.cyan(` ${runScriptCommand}\n`));
301
+ if (loopScript.skipped && options.overwriteLoop !== true) {
302
+ console.log(chalk.gray(' Existing loop file was kept. Use --overwrite-loop to regenerate.'));
303
+ }
304
+ console.log('');
305
+ }
159
306
  console.log(chalk.yellow('⚠️ IMPORTANT: Save these credentials!\n'));
160
307
  console.log(chalk.gray('API Key (keep secret):'));
161
308
  console.log(chalk.green(` ${payload.api_key || 'unavailable'}\n`));
162
309
  console.log(chalk.gray('Ownership Verification Link (optional trust setup):'));
163
- console.log(chalk.cyan(` ${inferClaimLink(payload) || 'unavailable'}\n`));
310
+ const ownershipLink = inferClaimLink(payload) || 'unavailable';
311
+ console.log(chalk.cyan(` ${ownershipLink}\n`));
312
+ console.log(chalk.bold.white('📣 Report To Coach (explicit step)'));
313
+ const objective = asString(payload.oracle?.tournament_objective) || 'pending objective';
314
+ const coachMessage = [
315
+ `Agent ${payload.name || 'unknown'} registered.`,
316
+ `Objective: ${objective}`,
317
+ `Ownership link: ${ownershipLink}`,
318
+ 'Request: provide strategy for the next 20 actions.',
319
+ ].join(' ');
320
+ console.log(chalk.gray('Copy/send this message to your human coach:'));
321
+ console.log(chalk.cyan(` ${coachMessage}\n`));
164
322
  console.log(chalk.cyan('━'.repeat(50)));
165
- console.log(chalk.bold.white('\n▶ Primary next action'));
166
- console.log(chalk.cyan(` ${getPrimaryNextAction(payload)}\n`));
167
- console.log(chalk.gray('Automation default: your agent should design + save a loop script, then run and observe it repeatedly (Bash day-0, Python durable).'));
168
- console.log(chalk.gray('Optional trust setup after gameplay starts: share the ownership verification link with your human.\n'));
169
323
  const oracle = payload.oracle;
170
324
  if (oracle) {
171
325
  console.log(chalk.bold.white('🔮 Oracle Briefing'));
@@ -218,7 +372,66 @@ export async function installSkill(skillName, options) {
218
372
  });
219
373
  console.log('');
220
374
  }
221
- const docsUrl = asString(payload.cli_handoff?.fallback_docs) || skill.skillUrl;
375
+ const coachObjectives = Array.isArray(payload.coach_objectives)
376
+ ? payload.coach_objectives
377
+ : [];
378
+ if (coachObjectives.length > 0) {
379
+ console.log(chalk.bold.white('🎯 Coach Objectives'));
380
+ coachObjectives.forEach((objective, index) => {
381
+ const title = asString(objective.title) || `Objective ${index + 1}`;
382
+ const status = asString(objective.status) || 'pending';
383
+ const rationale = asString(objective.rationale);
384
+ console.log(chalk.white(`${index + 1}. ${title} [${status}]`));
385
+ if (rationale) {
386
+ console.log(chalk.gray(` why: ${rationale}`));
387
+ }
388
+ });
389
+ console.log('');
390
+ }
391
+ const coachBadges = Array.isArray(payload.coach_badges)
392
+ ? payload.coach_badges
393
+ : [];
394
+ if (coachBadges.length > 0) {
395
+ console.log(chalk.bold.white('🏅 Strategy Badges'));
396
+ coachBadges.forEach((badge) => {
397
+ const title = asString(badge.title) || 'Badge';
398
+ const description = asString(badge.description);
399
+ const earned = badge.earned === true;
400
+ console.log(chalk.white(`- ${title}: ${earned ? 'earned' : 'locked'}`));
401
+ if (description) {
402
+ console.log(chalk.gray(` ${description}`));
403
+ }
404
+ });
405
+ console.log('');
406
+ }
407
+ const coachFeedback = payload.coach_feedback;
408
+ if (coachFeedback && typeof coachFeedback === 'object') {
409
+ const whatHappened = Array.isArray(coachFeedback.what_happened)
410
+ ? coachFeedback.what_happened.filter((line) => typeof line === 'string' && line.length > 0)
411
+ : [];
412
+ const happeningNow = Array.isArray(coachFeedback.what_is_happening_now)
413
+ ? coachFeedback.what_is_happening_now.filter((line) => typeof line === 'string' && line.length > 0)
414
+ : [];
415
+ const whatToDoNext = Array.isArray(coachFeedback.what_to_do_next)
416
+ ? coachFeedback.what_to_do_next.filter((line) => typeof line === 'string' && line.length > 0)
417
+ : [];
418
+ if (whatHappened.length > 0 || happeningNow.length > 0 || whatToDoNext.length > 0) {
419
+ console.log(chalk.bold.white('🧠 Agent-Human Feedback'));
420
+ if (whatHappened.length > 0) {
421
+ console.log(chalk.gray('What happened:'));
422
+ whatHappened.forEach((line) => console.log(chalk.gray(` - ${line}`)));
423
+ }
424
+ if (happeningNow.length > 0) {
425
+ console.log(chalk.gray('What is happening now:'));
426
+ happeningNow.forEach((line) => console.log(chalk.gray(` - ${line}`)));
427
+ }
428
+ if (whatToDoNext.length > 0) {
429
+ console.log(chalk.gray('What to do next:'));
430
+ whatToDoNext.forEach((line) => console.log(chalk.gray(` - ${line}`)));
431
+ }
432
+ console.log('');
433
+ }
434
+ }
222
435
  console.log(chalk.gray('Skill documentation:'));
223
436
  console.log(chalk.cyan(` ${docsUrl}\n`));
224
437
  console.log(chalk.gray('OpenClaw agent config:'));
@@ -3,8 +3,9 @@ import { formatOracleLines } from '../lib/formatters.js';
3
3
  export function registerOracleCommands(program) {
4
4
  program
5
5
  .command('oracle')
6
- .description('Read Oracle guidance: storyline, tournament objective, and onboarding outcomes')
6
+ .description('Read Oracle guidance (compact by default). Use --full for full briefing')
7
7
  .option('--all', 'Show all pending outcome steps instead of top 3')
8
+ .option('--full', 'Show full oracle briefing (detailed narrative, objectives, and feedback)')
8
9
  .option('--json', 'Print raw JSON response')
9
10
  .action(async (opts) => {
10
11
  const res = await api('/api/agents/me/oracle');
@@ -14,7 +15,10 @@ export function registerOracleCommands(program) {
14
15
  console.log(JSON.stringify(res.data, null, 2));
15
16
  return;
16
17
  }
17
- formatOracleLines(res.data, Boolean(opts.all)).forEach((line) => {
18
+ formatOracleLines(res.data, {
19
+ includeAllPending: Boolean(opts.all || opts.full),
20
+ verbose: Boolean(opts.full),
21
+ }).forEach((line) => {
18
22
  console.log(line);
19
23
  });
20
24
  });
@@ -240,6 +240,7 @@ export function registerPlanningCommands(program) {
240
240
  can_execute: asBoolean(claim.can_execute),
241
241
  can_afford: asBoolean(claim.can_afford),
242
242
  affordable_now: asBoolean(claim.can_execute) && asBoolean(claim.can_afford),
243
+ quote_source: asString(claim.quote_source) || 'unknown',
243
244
  reasons: Array.isArray(claim.reasons) ? claim.reasons : [],
244
245
  effective_cost: asRecord(claim.effective_cost) || {},
245
246
  missing_resources: Array.isArray(claim.missing_resources) ? claim.missing_resources : [],
@@ -255,6 +256,9 @@ export function registerPlanningCommands(program) {
255
256
  if (result.missing_resources.length > 0) {
256
257
  console.log(`Missing: ${result.missing_resources.join('; ')}`);
257
258
  }
259
+ if (result.quote_source !== 'rpc') {
260
+ console.log('Quote source: fallback (local estimate). If uncertain, run `clawcity claim` for authoritative cost.');
261
+ }
258
262
  printReasons(result.reasons);
259
263
  return;
260
264
  }
package/dist/index.js CHANGED
@@ -62,6 +62,10 @@ program
62
62
  .command('install <skill>')
63
63
  .description('Install a skill for your AI agent')
64
64
  .option('-n, --name <name>', 'Agent name to register')
65
+ .option('--mode <path>', 'Onboarding path: manual or scripted', 'manual')
66
+ .option('--with-loop', 'Alias for --mode scripted: generate a starter loop script')
67
+ .option('--loop-file <path>', 'Starter loop script output path', 'clawcity-loop.sh')
68
+ .option('--overwrite-loop', 'Overwrite existing loop file when generating scripted path')
65
69
  .action(async (skill, options) => {
66
70
  await installSkill(skill, options);
67
71
  });
@@ -13,5 +13,8 @@ export declare function formatTournamentJoinLine(data: UnknownRecord): string;
13
13
  export declare function formatTournamentDetailLines(data: UnknownRecord): string[];
14
14
  export declare function formatTournamentCreditsLines(data: UnknownRecord): string[];
15
15
  export declare function formatTournamentPerksLines(data: UnknownRecord): string[];
16
- export declare function formatOracleLines(data: UnknownRecord, includeAllPending?: boolean): string[];
16
+ export declare function formatOracleLines(data: UnknownRecord, options?: {
17
+ includeAllPending?: boolean;
18
+ verbose?: boolean;
19
+ }): string[];
17
20
  export {};
@@ -434,9 +434,15 @@ export function formatTournamentPerksLines(data) {
434
434
  }
435
435
  return lines;
436
436
  }
437
- export function formatOracleLines(data, includeAllPending = false) {
437
+ export function formatOracleLines(data, options) {
438
+ const includeAllPending = options?.includeAllPending === true;
439
+ const verbose = options?.verbose === true;
440
+ const automation = asRecord(data.automation_preflight);
438
441
  const contract = asRecord(data.contract);
439
442
  const oracle = asRecord(data.oracle);
443
+ const coachObjectives = asRecordArray(data.coach_objectives);
444
+ const coachBadges = asRecordArray(data.coach_badges);
445
+ const coachFeedback = asRecord(data.coach_feedback);
440
446
  const nextSteps = asRecordArray(data.next_steps);
441
447
  const allPendingSteps = asRecordArray(data.all_pending_steps);
442
448
  const title = asString(oracle?.title) || 'Oracle';
@@ -444,11 +450,34 @@ export function formatOracleLines(data, includeAllPending = false) {
444
450
  const objective = asString(oracle?.tournament_objective) || '';
445
451
  const completed = asNumber(contract?.completed_outcomes) ?? 0;
446
452
  const total = asNumber(contract?.total_outcomes) ?? 0;
447
- const lines = [
448
- `${title} | Outcomes: ${completed}/${total}`,
449
- narrative,
450
- `Objective: ${objective}`,
451
- ];
453
+ const lines = [`${title} | Outcomes: ${completed}/${total}`];
454
+ const automationHeadline = asString(automation?.headline);
455
+ const automationPartTitle = asString(automation?.part3_title);
456
+ const automationPartUrl = asString(automation?.part3_url);
457
+ const automationCommand = asString(automation?.recommended_command);
458
+ if (automationHeadline) {
459
+ lines.push(`Efficiency: ${automationHeadline}`);
460
+ }
461
+ if (automationPartTitle && automationPartUrl) {
462
+ lines.push(`${automationPartTitle}: ${automationPartUrl}`);
463
+ }
464
+ else if (automationPartUrl) {
465
+ lines.push(`Automation scripts: ${automationPartUrl}`);
466
+ }
467
+ if (automationCommand) {
468
+ lines.push(`Automation setup command: ${automationCommand}`);
469
+ }
470
+ if (verbose && narrative) {
471
+ lines.push(narrative);
472
+ }
473
+ lines.push(`Objective: ${objective}`);
474
+ const tournament = asRecord(oracle?.tournament);
475
+ if (tournament) {
476
+ const tournamentName = asString(tournament.name) || 'active';
477
+ const score = asNumber(tournament.current_score);
478
+ const rank = asNumber(tournament.current_rank);
479
+ lines.push(`Tournament: ${tournamentName} | score:${score ?? 0} | rank:${rank ?? 'unranked'}`);
480
+ }
452
481
  const pending = includeAllPending ? allPendingSteps : nextSteps;
453
482
  if (pending.length > 0) {
454
483
  lines.push(includeAllPending ? 'Pending steps:' : 'Next steps:');
@@ -459,7 +488,7 @@ export function formatOracleLines(data, includeAllPending = false) {
459
488
  lines.push(` ${index + 1}. ${titleStep}`);
460
489
  if (command)
461
490
  lines.push(` cmd: ${command}`);
462
- if (expected)
491
+ if (expected && verbose)
463
492
  lines.push(` expected: ${expected}`);
464
493
  });
465
494
  }
@@ -467,8 +496,79 @@ export function formatOracleLines(data, includeAllPending = false) {
467
496
  lines.push('All onboarding outcomes are complete.');
468
497
  }
469
498
  const prompt = asString(oracle?.starter_prompt);
470
- if (prompt) {
499
+ if (prompt && verbose) {
471
500
  lines.push(`Starter prompt: ${prompt}`);
472
501
  }
502
+ if (coachObjectives.length > 0 && verbose) {
503
+ lines.push('Coach objectives:');
504
+ coachObjectives.forEach((objective, index) => {
505
+ const objectiveTitle = asString(objective.title) || `Objective ${index + 1}`;
506
+ const status = asString(objective.status) || 'pending';
507
+ const rationale = asString(objective.rationale);
508
+ lines.push(` ${index + 1}. ${objectiveTitle} [${status}]`);
509
+ if (rationale)
510
+ lines.push(` why: ${rationale}`);
511
+ });
512
+ }
513
+ if (coachBadges.length > 0 && verbose) {
514
+ lines.push('Strategy badges:');
515
+ coachBadges.forEach((badge) => {
516
+ const title = asString(badge.title) || 'Badge';
517
+ const description = asString(badge.description);
518
+ const earned = badge.earned === true;
519
+ lines.push(` - ${title}: ${earned ? 'earned' : 'locked'}`);
520
+ if (description)
521
+ lines.push(` ${description}`);
522
+ });
523
+ }
524
+ if (coachFeedback) {
525
+ const whatHappened = Array.isArray(coachFeedback.what_happened)
526
+ ? coachFeedback.what_happened.filter((line) => typeof line === 'string' && line.length > 0)
527
+ : [];
528
+ const happeningNow = Array.isArray(coachFeedback.what_is_happening_now)
529
+ ? coachFeedback.what_is_happening_now.filter((line) => typeof line === 'string' && line.length > 0)
530
+ : [];
531
+ const whatToDoNext = Array.isArray(coachFeedback.what_to_do_next)
532
+ ? coachFeedback.what_to_do_next.filter((line) => typeof line === 'string' && line.length > 0)
533
+ : [];
534
+ if (whatHappened.length > 0 || happeningNow.length > 0 || whatToDoNext.length > 0) {
535
+ if (verbose) {
536
+ lines.push('Agent-human feedback:');
537
+ if (whatHappened.length > 0) {
538
+ lines.push(' What happened:');
539
+ whatHappened.forEach((line) => lines.push(` - ${line}`));
540
+ }
541
+ if (happeningNow.length > 0) {
542
+ lines.push(' What is happening now:');
543
+ happeningNow.forEach((line) => lines.push(` - ${line}`));
544
+ }
545
+ if (whatToDoNext.length > 0) {
546
+ lines.push(' What to do next:');
547
+ whatToDoNext.forEach((line) => lines.push(` - ${line}`));
548
+ }
549
+ }
550
+ else {
551
+ if (happeningNow.length > 0) {
552
+ lines.push(`Now: ${happeningNow[0]}`);
553
+ }
554
+ if (whatToDoNext.length > 0) {
555
+ lines.push(`Next: ${whatToDoNext[0]}`);
556
+ }
557
+ }
558
+ }
559
+ }
560
+ if (!verbose && coachBadges.length > 0) {
561
+ const earnedCount = coachBadges.filter((badge) => badge.earned === true).length;
562
+ lines.push(`Strategy badges: ${earnedCount}/${coachBadges.length} earned`);
563
+ }
564
+ if (!verbose && coachObjectives.length > 0) {
565
+ const pendingObjective = coachObjectives.find((objective) => asString(objective.status) !== 'complete');
566
+ if (pendingObjective) {
567
+ const titleStep = asString(pendingObjective.title);
568
+ if (titleStep) {
569
+ lines.push(`Coach objective focus: ${titleStep}`);
570
+ }
571
+ }
572
+ }
473
573
  return lines;
474
574
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawcity",
3
- "version": "2.5.2",
3
+ "version": "2.5.4",
4
4
  "description": "Agent-first CLI for ClawCity gameplay, tournaments, and public game APIs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",