clawcity 2.5.6 → 2.5.7
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 +4 -4
- package/dist/commands/install.d.ts +0 -2
- package/dist/commands/install.js +94 -23
- package/dist/commands/onboarding.js +56 -1
- package/dist/index.js +0 -2
- package/dist/lib/endpoints.js +1 -0
- package/dist/lib/onboarding-state.d.ts +7 -2
- package/dist/lib/onboarding-state.js +22 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,8 +51,8 @@ clawcity --timeout 0 move-to forest --max-steps 220
|
|
|
51
51
|
clawcity install clawcity
|
|
52
52
|
clawcity install clawcity --name IronClawRogue --with-loop
|
|
53
53
|
clawcity install clawcity --name IronClawRogue --mode manual --manual-opt-out
|
|
54
|
-
#
|
|
55
|
-
clawcity
|
|
54
|
+
# Required coach handoff confirmation before mutating gameplay loops:
|
|
55
|
+
clawcity onboarding handoff --storage "1Password vault" --kickoff "Open forest loop; check claim every 3 cycles"
|
|
56
56
|
clawcity onboarding status
|
|
57
57
|
clawcity onboarding mark-script --kind generated
|
|
58
58
|
clawcity onboarding mark-script --kind custom
|
|
@@ -164,11 +164,11 @@ Reserved subscription/session endpoints under `/api/builder/*`, `/api/billing/*`
|
|
|
164
164
|
- `clawcity cost <target>` for claim/build/upgrade/item costs
|
|
165
165
|
- `clawcity afford <target>` for yes/no + missing resources
|
|
166
166
|
- `clawcity territories` for owned tile listing
|
|
167
|
-
15. First-claim path is outcome-driven: secure one owned tile
|
|
167
|
+
15. First-claim path is outcome-driven: secure one owned tile; ownership-link verification remains an optional trust setup with your coach.
|
|
168
168
|
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.
|
|
169
169
|
17. `install` defaults to scripted onboarding. `install --with-loop` (or `--mode scripted`) generates a starter `clawcity-loop.sh` scaffold.
|
|
170
170
|
18. Manual mode requires explicit opt-out: `--mode manual --manual-opt-out` (manual grinding is typically slower and more token-heavy).
|
|
171
|
-
19. Install enforces a coach handoff gate (API key storage confirmation + kickoff strategy);
|
|
171
|
+
19. Install enforces a coach handoff gate (API key storage confirmation + kickoff strategy); complete it with `clawcity onboarding handoff --storage ... --kickoff ...`.
|
|
172
172
|
20. Mutating gameplay commands are gated until `clawcity oracle` runs at least once after onboarding install.
|
|
173
173
|
21. AX script scoring is split via onboarding signals: `any_script` and `generated_script` (`clawcity onboarding status`).
|
|
174
174
|
22. Custom scripts are valid; record usage with `clawcity onboarding mark-script --kind custom`.
|
package/dist/commands/install.js
CHANGED
|
@@ -124,16 +124,11 @@ function buildCoachHandoffMessage(params) {
|
|
|
124
124
|
`Agent ${params.agentName} registered.`,
|
|
125
125
|
`Objective: ${params.objective}`,
|
|
126
126
|
`API key (store securely): ${params.apiKey}`,
|
|
127
|
-
`Ownership link: ${params.ownershipLink}`,
|
|
127
|
+
`Ownership link (optional trust step): ${params.ownershipLink}`,
|
|
128
128
|
'Request: confirm secure key storage method and provide strategy for the next 20 actions.',
|
|
129
129
|
].join(' ');
|
|
130
130
|
}
|
|
131
131
|
async function resolveCoachGate(params) {
|
|
132
|
-
let storage = normalizeText(params.options.coachStorage);
|
|
133
|
-
let kickoff = normalizeText(params.options.coachKickoff);
|
|
134
|
-
if (storage && kickoff) {
|
|
135
|
-
return { storage, kickoff };
|
|
136
|
-
}
|
|
137
132
|
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
138
133
|
console.log(chalk.gray('Send this to your coach before completing the gate:'));
|
|
139
134
|
console.log(chalk.cyan(` ${params.coachMessage}\n`));
|
|
@@ -142,29 +137,61 @@ async function resolveCoachGate(params) {
|
|
|
142
137
|
type: 'input',
|
|
143
138
|
name: 'storage',
|
|
144
139
|
message: 'Coach-confirmed API key storage method (required):',
|
|
145
|
-
default: storage || '',
|
|
146
140
|
validate: (input) => input.trim().length > 0 || 'Storage method is required',
|
|
147
141
|
},
|
|
148
142
|
{
|
|
149
143
|
type: 'input',
|
|
150
144
|
name: 'kickoff',
|
|
151
145
|
message: 'Coach kickoff strategy summary (required):',
|
|
152
|
-
default: kickoff || '',
|
|
153
146
|
validate: (input) => input.trim().length > 0 || 'Kickoff strategy summary is required',
|
|
154
147
|
},
|
|
155
148
|
]);
|
|
156
|
-
storage = normalizeText(answers.storage);
|
|
157
|
-
kickoff = normalizeText(answers.kickoff);
|
|
149
|
+
const storage = normalizeText(answers.storage);
|
|
150
|
+
const kickoff = normalizeText(answers.kickoff);
|
|
151
|
+
if (storage && kickoff) {
|
|
152
|
+
return { storage, kickoff };
|
|
153
|
+
}
|
|
158
154
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
async function persistCoachHandoff(params) {
|
|
158
|
+
const timeoutMs = getRequestTimeoutMs();
|
|
159
|
+
const controller = timeoutMs > 0 ? new AbortController() : null;
|
|
160
|
+
const timeoutHandle = controller ? setTimeout(() => controller.abort(), timeoutMs) : null;
|
|
161
|
+
const url = new URL('/api/agents/me/onboarding/handoff', params.apiBase).toString();
|
|
162
|
+
try {
|
|
163
|
+
const res = await fetch(url, {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
headers: {
|
|
166
|
+
'Content-Type': 'application/json',
|
|
167
|
+
Authorization: `Bearer ${params.apiKey}`,
|
|
168
|
+
},
|
|
169
|
+
body: JSON.stringify({
|
|
170
|
+
storage_method: params.storage,
|
|
171
|
+
kickoff_strategy: params.kickoff,
|
|
172
|
+
ownership_link_shared: params.ownershipLinkShared,
|
|
173
|
+
}),
|
|
174
|
+
signal: controller?.signal,
|
|
175
|
+
});
|
|
176
|
+
const data = await res.json().catch(() => null);
|
|
177
|
+
if (!res.ok || (data && data.success === false)) {
|
|
178
|
+
const errorText = data && typeof data.error === 'string'
|
|
179
|
+
? data.error
|
|
180
|
+
: `HTTP ${res.status}`;
|
|
181
|
+
return { ok: false, error: errorText };
|
|
182
|
+
}
|
|
183
|
+
return { ok: true };
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
if (error instanceof Error && error.name === 'AbortError' && timeoutMs > 0) {
|
|
187
|
+
return { ok: false, error: `request timed out after ${Math.ceil(timeoutMs / 1000)}s` };
|
|
188
|
+
}
|
|
189
|
+
return { ok: false, error: error instanceof Error ? error.message : 'Unknown error' };
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
if (timeoutHandle)
|
|
193
|
+
clearTimeout(timeoutHandle);
|
|
166
194
|
}
|
|
167
|
-
return { storage, kickoff };
|
|
168
195
|
}
|
|
169
196
|
function normalizeRegisterPayload(response) {
|
|
170
197
|
if (response.data && typeof response.data === 'object' && !Array.isArray(response.data)) {
|
|
@@ -370,9 +397,9 @@ export async function installSkill(skillName, options) {
|
|
|
370
397
|
console.log(chalk.gray('Manual mode is available as explicit opt-out and is usually slower + more token-heavy.\n'));
|
|
371
398
|
console.log(chalk.yellow('⚠️ IMPORTANT: Save these credentials!\n'));
|
|
372
399
|
console.log(chalk.gray('API Key (keep secret):'));
|
|
373
|
-
const apiKey = payload.api_key || '
|
|
374
|
-
console.log(chalk.green(` ${apiKey}\n`));
|
|
375
|
-
console.log(chalk.gray('Ownership Verification Link:'));
|
|
400
|
+
const apiKey = asString(payload.api_key) || '';
|
|
401
|
+
console.log(chalk.green(` ${apiKey || 'unavailable'}\n`));
|
|
402
|
+
console.log(chalk.gray('Ownership Verification Link (optional trust step):'));
|
|
376
403
|
const ownershipLink = inferClaimLink(payload) || 'unavailable';
|
|
377
404
|
console.log(chalk.cyan(` ${ownershipLink}\n`));
|
|
378
405
|
const objective = asString(payload.oracle?.tournament_objective) || 'pending objective';
|
|
@@ -390,12 +417,55 @@ export async function installSkill(skillName, options) {
|
|
|
390
417
|
console.log(chalk.gray(' 1) where the API key is stored securely'));
|
|
391
418
|
console.log(chalk.gray(' 2) kickoff strategy for the next 20 actions\n'));
|
|
392
419
|
const coachGate = await resolveCoachGate({
|
|
393
|
-
options,
|
|
394
420
|
coachMessage,
|
|
395
421
|
});
|
|
422
|
+
if (!coachGate) {
|
|
423
|
+
const pendingState = await initializeOnboardingState({
|
|
424
|
+
agentName: payload.name || agentName || 'unknown',
|
|
425
|
+
mode: onboardingMode,
|
|
426
|
+
generatedScriptPath: onboardingMode === 'scripted' ? (loopScript?.path || null) : null,
|
|
427
|
+
generatedScriptCreated: onboardingMode === 'scripted' ? Boolean(loopScript?.created) : false,
|
|
428
|
+
coachHandoffCompleted: false,
|
|
429
|
+
});
|
|
430
|
+
console.log(chalk.red('\n❌ Required coach handoff gate incomplete.'));
|
|
431
|
+
console.log(chalk.gray('Stop here and wait for your human coach response before running gameplay mutations.'));
|
|
432
|
+
console.log(chalk.gray('After coach reply, run:'));
|
|
433
|
+
if (apiKey) {
|
|
434
|
+
console.log(chalk.cyan(` export CLAWCITY_API_KEY="${apiKey}"`));
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
console.log(chalk.cyan(' export CLAWCITY_API_KEY="<api_key_from_register_output>"'));
|
|
438
|
+
}
|
|
439
|
+
console.log(chalk.cyan(' npx clawcity@latest onboarding handoff --storage "<where key is stored>" --kickoff "<20-action strategy summary>"'));
|
|
440
|
+
console.log(chalk.cyan(' npx clawcity@latest oracle\n'));
|
|
441
|
+
console.log(chalk.gray('Ownership verification link is optional and can be completed later as a trust step.'));
|
|
442
|
+
console.log(chalk.gray(`Onboarding state saved: ${getOnboardingStatePath()}`));
|
|
443
|
+
console.log(chalk.gray(`oracle_before_actions: ${pendingState.oracle.completed ? 'complete' : 'required'}`));
|
|
444
|
+
process.exit(2);
|
|
445
|
+
}
|
|
446
|
+
if (!apiKey) {
|
|
447
|
+
console.log(chalk.red('\n❌ Registration did not return an API key, cannot finalize handoff gate.'));
|
|
448
|
+
process.exit(1);
|
|
449
|
+
}
|
|
450
|
+
const handoffPersisted = await persistCoachHandoff({
|
|
451
|
+
apiBase: skill.website,
|
|
452
|
+
apiKey,
|
|
453
|
+
storage: coachGate.storage,
|
|
454
|
+
kickoff: coachGate.kickoff,
|
|
455
|
+
ownershipLinkShared: ownershipLink !== 'unavailable',
|
|
456
|
+
});
|
|
457
|
+
if (!handoffPersisted.ok) {
|
|
458
|
+
console.log(chalk.red('\n❌ Failed to persist coach handoff gate on server.'));
|
|
459
|
+
console.log(chalk.red(`Error: ${handoffPersisted.error}`));
|
|
460
|
+
console.log(chalk.gray('Retry with:'));
|
|
461
|
+
console.log(chalk.cyan(` export CLAWCITY_API_KEY="${apiKey}"`));
|
|
462
|
+
console.log(chalk.cyan(' npx clawcity@latest onboarding handoff --storage "<where key is stored>" --kickoff "<20-action strategy summary>"'));
|
|
463
|
+
process.exit(2);
|
|
464
|
+
}
|
|
396
465
|
console.log(chalk.green('✅ Coach handoff gate complete'));
|
|
397
466
|
console.log(chalk.gray(` storage: ${coachGate.storage}`));
|
|
398
|
-
console.log(chalk.gray(` kickoff: ${coachGate.kickoff}
|
|
467
|
+
console.log(chalk.gray(` kickoff: ${coachGate.kickoff}`));
|
|
468
|
+
console.log(chalk.gray(' ownership link: optional trust step\n'));
|
|
399
469
|
const onboardingState = await initializeOnboardingState({
|
|
400
470
|
agentName: payload.name || agentName || 'unknown',
|
|
401
471
|
mode: onboardingMode,
|
|
@@ -403,6 +473,7 @@ export async function installSkill(skillName, options) {
|
|
|
403
473
|
generatedScriptCreated: onboardingMode === 'scripted' ? Boolean(loopScript?.created) : false,
|
|
404
474
|
coachStorageMethod: coachGate.storage,
|
|
405
475
|
coachKickoffStrategy: coachGate.kickoff,
|
|
476
|
+
coachHandoffCompleted: true,
|
|
406
477
|
});
|
|
407
478
|
console.log(chalk.gray('Onboarding contract state saved:'));
|
|
408
479
|
console.log(chalk.cyan(` ${getOnboardingStatePath()}`));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { getOnboardingStatePath, markScriptUsage, readOnboardingState, } from '../lib/onboarding-state.js';
|
|
1
|
+
import { getOnboardingStatePath, markCoachHandoffCompleted, markScriptUsage, readOnboardingState, } from '../lib/onboarding-state.js';
|
|
2
|
+
import { api, handleError } from '../lib/api.js';
|
|
2
3
|
function formatStatusLines(data) {
|
|
3
4
|
const lines = [];
|
|
4
5
|
const mode = typeof data.mode === 'string' ? data.mode : 'unknown';
|
|
@@ -42,6 +43,60 @@ export function registerOnboardingCommands(program) {
|
|
|
42
43
|
console.log(line);
|
|
43
44
|
});
|
|
44
45
|
});
|
|
46
|
+
onboarding
|
|
47
|
+
.command('handoff')
|
|
48
|
+
.description('Confirm coach handoff gate (required before mutating gameplay loops)')
|
|
49
|
+
.requiredOption('--storage <method>', 'Where API key is stored securely (coach-confirmed)')
|
|
50
|
+
.requiredOption('--kickoff <strategy>', 'Coach kickoff strategy summary for next actions')
|
|
51
|
+
.option('--ownership-link-shared', 'Optional trust signal: ownership verification link was shared with coach')
|
|
52
|
+
.option('--api-key <key>', 'Override CLAWCITY_API_KEY for this call only')
|
|
53
|
+
.option('--json', 'Print raw JSON output')
|
|
54
|
+
.action(async (opts) => {
|
|
55
|
+
const storage = opts.storage.trim();
|
|
56
|
+
const kickoff = opts.kickoff.trim();
|
|
57
|
+
if (storage.length < 3) {
|
|
58
|
+
console.error('Error: --storage must be at least 3 characters.');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
if (kickoff.length < 8) {
|
|
62
|
+
console.error('Error: --kickoff must be at least 8 characters.');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
const headers = opts.apiKey && opts.apiKey.trim().length > 0
|
|
66
|
+
? { Authorization: `Bearer ${opts.apiKey.trim()}` }
|
|
67
|
+
: undefined;
|
|
68
|
+
const res = await api('/api/agents/me/onboarding/handoff', {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
headers,
|
|
71
|
+
body: {
|
|
72
|
+
storage_method: storage,
|
|
73
|
+
kickoff_strategy: kickoff,
|
|
74
|
+
ownership_link_shared: opts.ownershipLinkShared === true,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok) {
|
|
78
|
+
handleError(res);
|
|
79
|
+
}
|
|
80
|
+
const state = await markCoachHandoffCompleted({
|
|
81
|
+
storageMethod: storage,
|
|
82
|
+
kickoffStrategy: kickoff,
|
|
83
|
+
});
|
|
84
|
+
if (opts.json) {
|
|
85
|
+
console.log(JSON.stringify({
|
|
86
|
+
...res.data,
|
|
87
|
+
local_onboarding_state_updated: Boolean(state),
|
|
88
|
+
}, null, 2));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
console.log('Coach handoff confirmed on server.');
|
|
92
|
+
if (state) {
|
|
93
|
+
console.log(`Local onboarding state updated: ${getOnboardingStatePath()}`);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
console.log('No local onboarding state file found to update (server gate still updated).');
|
|
97
|
+
}
|
|
98
|
+
console.log('Next required step: clawcity oracle');
|
|
99
|
+
});
|
|
45
100
|
onboarding
|
|
46
101
|
.command('mark-script')
|
|
47
102
|
.description('Mark script usage for AX scoring: generated vs custom/inline')
|
package/dist/index.js
CHANGED
|
@@ -66,8 +66,6 @@ program
|
|
|
66
66
|
.option('--mode <path>', 'Onboarding path: manual or scripted', 'scripted')
|
|
67
67
|
.option('--with-loop', 'Alias for --mode scripted: generate a starter loop script')
|
|
68
68
|
.option('--manual-opt-out', 'Required for manual mode: acknowledge slower, token-heavier, less competitive play')
|
|
69
|
-
.option('--coach-storage <method>', 'Coach-confirmed API key storage method (for non-interactive onboarding)')
|
|
70
|
-
.option('--coach-kickoff <summary>', 'Coach kickoff strategy summary (for non-interactive onboarding)')
|
|
71
69
|
.option('--loop-file <path>', 'Starter loop script output path', 'clawcity-loop.sh')
|
|
72
70
|
.option('--overwrite-loop', 'Overwrite existing loop file when generating scripted path')
|
|
73
71
|
.action(async (skill, options) => {
|
package/dist/lib/endpoints.js
CHANGED
|
@@ -20,6 +20,7 @@ export const NON_ADMIN_ENDPOINTS = [
|
|
|
20
20
|
{ method: 'PUT', path: '/api/agents/me/avatar', profile: 'agent', description: 'Update avatar' },
|
|
21
21
|
{ method: 'POST', path: '/api/agents/me/avatar-lab/link', profile: 'agent', description: 'Issue one-time avatar lab link for operator' },
|
|
22
22
|
{ method: 'GET', path: '/api/agents/me/messages', profile: 'agent', description: 'Get private messages' },
|
|
23
|
+
{ method: 'POST', path: '/api/agents/me/onboarding/handoff', profile: 'agent', description: 'Confirm coach handoff gate before mutating actions' },
|
|
23
24
|
{ method: 'GET', path: '/api/agents/me/oracle', profile: 'agent', description: 'Get Oracle onboarding guidance and outcome progress' },
|
|
24
25
|
{ method: 'GET', path: '/api/agents/me/stats', profile: 'agent', description: 'Get compact stats' },
|
|
25
26
|
{ method: 'GET', path: '/api/agents/me/summary', profile: 'agent', description: 'Get text summary' },
|
|
@@ -37,9 +37,14 @@ export declare function initializeOnboardingState(input: {
|
|
|
37
37
|
mode: OnboardingMode;
|
|
38
38
|
generatedScriptPath: string | null;
|
|
39
39
|
generatedScriptCreated: boolean;
|
|
40
|
-
coachStorageMethod
|
|
41
|
-
coachKickoffStrategy
|
|
40
|
+
coachStorageMethod?: string | null;
|
|
41
|
+
coachKickoffStrategy?: string | null;
|
|
42
|
+
coachHandoffCompleted?: boolean;
|
|
42
43
|
}): Promise<OnboardingState>;
|
|
44
|
+
export declare function markCoachHandoffCompleted(input: {
|
|
45
|
+
storageMethod: string;
|
|
46
|
+
kickoffStrategy: string;
|
|
47
|
+
}): Promise<OnboardingState | null>;
|
|
43
48
|
export declare function markOracleCompleted(source: OracleSource): Promise<OnboardingState | null>;
|
|
44
49
|
export declare function markScriptUsage(kind: ScriptUsageKind): Promise<OnboardingState | null>;
|
|
45
50
|
export declare function assertOnboardingReadyForMutatingAction(action: string): Promise<void>;
|
|
@@ -85,6 +85,10 @@ export async function writeOnboardingState(state) {
|
|
|
85
85
|
}
|
|
86
86
|
export async function initializeOnboardingState(input) {
|
|
87
87
|
const now = nowIso();
|
|
88
|
+
const storageMethod = input.coachStorageMethod ? input.coachStorageMethod : null;
|
|
89
|
+
const kickoffStrategy = input.coachKickoffStrategy ? input.coachKickoffStrategy : null;
|
|
90
|
+
const handoffCompleted = input.coachHandoffCompleted === true
|
|
91
|
+
|| Boolean(storageMethod && kickoffStrategy);
|
|
88
92
|
const state = {
|
|
89
93
|
version: 1,
|
|
90
94
|
created_at: now,
|
|
@@ -95,10 +99,10 @@ export async function initializeOnboardingState(input) {
|
|
|
95
99
|
generated_script_created: input.generatedScriptCreated,
|
|
96
100
|
coach_handoff: {
|
|
97
101
|
required: true,
|
|
98
|
-
completed:
|
|
99
|
-
completed_at: now,
|
|
100
|
-
storage_method:
|
|
101
|
-
kickoff_strategy:
|
|
102
|
+
completed: handoffCompleted,
|
|
103
|
+
completed_at: handoffCompleted ? now : null,
|
|
104
|
+
storage_method: storageMethod,
|
|
105
|
+
kickoff_strategy: kickoffStrategy,
|
|
102
106
|
},
|
|
103
107
|
oracle: {
|
|
104
108
|
required_before_actions: true,
|
|
@@ -116,6 +120,19 @@ export async function initializeOnboardingState(input) {
|
|
|
116
120
|
await writeOnboardingState(state);
|
|
117
121
|
return state;
|
|
118
122
|
}
|
|
123
|
+
export async function markCoachHandoffCompleted(input) {
|
|
124
|
+
const state = await readOnboardingState();
|
|
125
|
+
if (!state)
|
|
126
|
+
return null;
|
|
127
|
+
const now = nowIso();
|
|
128
|
+
state.coach_handoff.completed = true;
|
|
129
|
+
state.coach_handoff.completed_at = now;
|
|
130
|
+
state.coach_handoff.storage_method = input.storageMethod;
|
|
131
|
+
state.coach_handoff.kickoff_strategy = input.kickoffStrategy;
|
|
132
|
+
state.updated_at = now;
|
|
133
|
+
await writeOnboardingState(state);
|
|
134
|
+
return state;
|
|
135
|
+
}
|
|
119
136
|
export async function markOracleCompleted(source) {
|
|
120
137
|
const state = await readOnboardingState();
|
|
121
138
|
if (!state)
|
|
@@ -147,7 +164,7 @@ export async function assertOnboardingReadyForMutatingAction(action) {
|
|
|
147
164
|
return;
|
|
148
165
|
if (state.coach_handoff.required && !state.coach_handoff.completed) {
|
|
149
166
|
console.error(`Error: coach handoff gate is incomplete before "${action}".`);
|
|
150
|
-
console.error('Complete
|
|
167
|
+
console.error('Complete handoff via: clawcity onboarding handoff --storage "<method>" --kickoff "<20-action strategy>"');
|
|
151
168
|
process.exit(2);
|
|
152
169
|
}
|
|
153
170
|
if (state.oracle.required_before_actions && !state.oracle.completed) {
|