@ttctl/cli 0.1.0-rc.6 → 0.1.0-rc.8

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 (55) hide show
  1. package/dist/commands/applications/availability-request.d.ts +47 -0
  2. package/dist/commands/applications/availability-request.d.ts.map +1 -0
  3. package/dist/commands/applications/availability-request.js +98 -0
  4. package/dist/commands/applications/availability-request.js.map +1 -0
  5. package/dist/commands/applications/confirm.d.ts +32 -2
  6. package/dist/commands/applications/confirm.d.ts.map +1 -1
  7. package/dist/commands/applications/confirm.js +194 -1
  8. package/dist/commands/applications/confirm.js.map +1 -1
  9. package/dist/commands/applications/index.d.ts +6 -2
  10. package/dist/commands/applications/index.d.ts.map +1 -1
  11. package/dist/commands/applications/index.js +112 -3
  12. package/dist/commands/applications/index.js.map +1 -1
  13. package/dist/commands/applications/interview.d.ts +159 -0
  14. package/dist/commands/applications/interview.d.ts.map +1 -0
  15. package/dist/commands/applications/interview.js +396 -0
  16. package/dist/commands/applications/interview.js.map +1 -0
  17. package/dist/commands/jobs/apply.d.ts +194 -0
  18. package/dist/commands/jobs/apply.d.ts.map +1 -0
  19. package/dist/commands/jobs/apply.js +505 -0
  20. package/dist/commands/jobs/apply.js.map +1 -0
  21. package/dist/commands/jobs/index.d.ts +19 -5
  22. package/dist/commands/jobs/index.d.ts.map +1 -1
  23. package/dist/commands/jobs/index.js +78 -6
  24. package/dist/commands/jobs/index.js.map +1 -1
  25. package/dist/commands/jobs/show.d.ts +63 -2
  26. package/dist/commands/jobs/show.d.ts.map +1 -1
  27. package/dist/commands/jobs/show.js +77 -5
  28. package/dist/commands/jobs/show.js.map +1 -1
  29. package/dist/commands/payments/index.d.ts +5 -2
  30. package/dist/commands/payments/index.d.ts.map +1 -1
  31. package/dist/commands/payments/index.js +27 -4
  32. package/dist/commands/payments/index.js.map +1 -1
  33. package/dist/commands/payments/rate.d.ts +16 -0
  34. package/dist/commands/payments/rate.d.ts.map +1 -1
  35. package/dist/commands/payments/rate.js +30 -0
  36. package/dist/commands/payments/rate.js.map +1 -1
  37. package/dist/commands/payments/summary.d.ts +17 -0
  38. package/dist/commands/payments/summary.d.ts.map +1 -0
  39. package/dist/commands/payments/summary.js +42 -0
  40. package/dist/commands/payments/summary.js.map +1 -0
  41. package/dist/commands/profile/employment/index.d.ts.map +1 -1
  42. package/dist/commands/profile/employment/index.js +23 -3
  43. package/dist/commands/profile/employment/index.js.map +1 -1
  44. package/dist/commands/profile/reviews/index.d.ts.map +1 -1
  45. package/dist/commands/profile/reviews/index.js +5 -1
  46. package/dist/commands/profile/reviews/index.js.map +1 -1
  47. package/dist/commands/profile/reviews/submit-for-review.d.ts +8 -0
  48. package/dist/commands/profile/reviews/submit-for-review.d.ts.map +1 -1
  49. package/dist/commands/profile/reviews/submit-for-review.js +15 -1
  50. package/dist/commands/profile/reviews/submit-for-review.js.map +1 -1
  51. package/dist/lib/json-input.d.ts +121 -0
  52. package/dist/lib/json-input.d.ts.map +1 -0
  53. package/dist/lib/json-input.js +245 -0
  54. package/dist/lib/json-input.js.map +1 -0
  55. package/package.json +2 -2
@@ -0,0 +1,505 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { applications } from "@ttctl/core";
4
+ import { z } from "zod";
5
+ import { getCliDryRun } from "../../lib/dry-run.js";
6
+ import { emitDryRunSuccess, emitErrorAndExit, emitUpdateSuccess } from "../../lib/envelopes.js";
7
+ import { JsonInputError, parseAsRecovered, readJsonInput } from "../../lib/json-input.js";
8
+ import { emitResult } from "../../lib/output.js";
9
+ import { handleApplicationsError, loadAuthTokenOrExit } from "../applications/shared.js";
10
+ const DECIMAL_PATTERN = /^\d+(\.\d+)?$/;
11
+ /**
12
+ * Stage-2 Zod schemas for `--answers-file` / `--pitch-file` payload
13
+ * validation (#438). Materialized once at module load and reused across
14
+ * parse invocations — the codegen factory pattern returns a fresh
15
+ * `z.object(...)` per call, so we cache the result.
16
+ *
17
+ * `.strict()` is mandatory at this boundary: codegen emits schemas with
18
+ * default "strip unknown" semantics; the AC requires extra unknown keys
19
+ * to reject with a field-path error.
20
+ */
21
+ const STRICT_MATCHER_ANSWER_SCHEMA = applications.JobPositionAnswerInputSchema().strict();
22
+ const STRICT_EXPERTISE_ANSWER_SCHEMA = applications.JobExpertiseAnswerInputSchema().strict();
23
+ const STRICT_PITCH_INPUT_SCHEMA = applications.PitchInputSchema().strict();
24
+ const MATCHER_ANSWERS_ARRAY_SCHEMA = z.array(STRICT_MATCHER_ANSWER_SCHEMA);
25
+ const EXPERTISE_ANSWERS_ARRAY_SCHEMA = z.array(STRICT_EXPERTISE_ANSWER_SCHEMA);
26
+ /**
27
+ * Action handler for `ttctl jobs apply <id>` (#430).
28
+ *
29
+ * Issues the direct `JobApply` mutation via `applications.apply()` (per
30
+ * ADR-008 § Decision Part 5: the service module is `applications`; the
31
+ * user-facing verb lives on `jobs`). The handler enforces three
32
+ * pre-wire gates in order:
33
+ *
34
+ * 1. **`--show-questions` preview** — when set, fetches `applyData`
35
+ * + `applyQuestions` in parallel and emits the projection;
36
+ * returns BEFORE the consent gate (read-only path).
37
+ * 2. **Consent gate** — `--consent` is REQUIRED per ADR-008 § Decision
38
+ * Part 4. Absence raises `CONSENT_REQUIRED` with no wire call
39
+ * issued. The service's own runtime check at `apply()` is
40
+ * defense-in-depth.
41
+ * 3. **`--rate` validation** — decimal-string format enforced via
42
+ * {@link DECIMAL_PATTERN} (mirrors the `applications confirm`
43
+ * pattern). Bad input refuses with `MUTATION_ERROR`.
44
+ *
45
+ * `--answers-file` / `--pitch-file` are loaded + wrapper-shape-validated
46
+ * BEFORE the apply call (mirrors #428 confirm semantics): malformed
47
+ * JSON or wrong wrapper shape refuses with `VALIDATION_ERROR` and no
48
+ * mutation is issued.
49
+ *
50
+ * **DESTRUCTIVE** — applying to a job creates a `JobApplication`
51
+ * record. No `withdraw` operation is available on the wire (per ADR-008
52
+ * § What We're NOT Solving). Prefer `--dry-run` to preview the wire
53
+ * payload first; the AC scenarios pin the dry-run preview shape.
54
+ */
55
+ export async function runJobsApply(id, opts) {
56
+ const token = await loadAuthTokenOrExit("jobs apply", opts.output);
57
+ const dryRun = getCliDryRun();
58
+ // ---- 1. --show-questions preview (BEFORE consent gate) ----
59
+ // Read-only path: fetches the pre-apply context the user would need
60
+ // to author an answers-file payload (REQ-Q3). Skips both the consent
61
+ // gate AND the JobApply mutation; matches the AC scenario
62
+ // "--show-questions issues pre-fetch but skips mutation".
63
+ if (opts.showQuestions === true) {
64
+ let preApply;
65
+ let questions;
66
+ try {
67
+ [preApply, questions] = await Promise.all([
68
+ applications.applyData(token, id),
69
+ applications.applyQuestions(token, id),
70
+ ]);
71
+ }
72
+ catch (err) {
73
+ handleApplicationsError("jobs apply", err, opts.output);
74
+ }
75
+ const projection = {
76
+ jobId: id,
77
+ canApply: preApply.canApply,
78
+ applyErrors: preApply.applyErrors,
79
+ suggestedRate: preApply.suggestedRate,
80
+ rateValidation: preApply.rateValidation,
81
+ matcherQuestions: questions.matcherQuestions,
82
+ expertiseQuestions: questions.expertiseQuestions,
83
+ };
84
+ // --suggest-answers + --show-questions: fetch the suggestions
85
+ // alongside the question inventory. Falls into the same graceful
86
+ // degradation pattern as the apply-path branch — failures of the
87
+ // suggestion fetch emit a stderr warning and the preview output
88
+ // omits the `suggestions` section. The standard `applyQuestions`
89
+ // failure path (above) already short-circuited via
90
+ // handleApplicationsError, so reaching here means the inventory
91
+ // resolved.
92
+ if (opts.suggestAnswers === true) {
93
+ const suggestions = await fetchSuggestionsOrWarn(token, id);
94
+ if (suggestions !== null)
95
+ projection.suggestions = suggestions;
96
+ }
97
+ emitResult(projection, opts.output, {
98
+ pretty: (data) => formatShowQuestions(data),
99
+ });
100
+ return;
101
+ }
102
+ // ---- 2. Consent gate (CLI-level refusal; no wire call) ----
103
+ // The service's own check (`applications.apply` line 2598) is
104
+ // defense-in-depth. Refusing at the CLI layer keeps the unit-test
105
+ // signal clean — `applications.apply` is never called when consent
106
+ // is absent, matching the AC "no JobApply wire mutation is sent".
107
+ if (opts.consent !== true) {
108
+ emitErrorAndExit({
109
+ operation: "jobs.apply",
110
+ format: opts.output,
111
+ errors: [
112
+ {
113
+ code: "CONSENT_REQUIRED",
114
+ message: "--consent is required to apply: this flag represents your acceptance of Toptal's apply terms (a legal-compliance attestation). Auto-filling on your behalf is forbidden per ADR-008.",
115
+ hint: "Re-run with --consent to attest you have read and accepted Toptal's apply terms. Use --dry-run to preview the wire payload first.",
116
+ },
117
+ ],
118
+ prettySummary: "jobs apply failed (CONSENT_REQUIRED): --consent is required to apply.",
119
+ });
120
+ }
121
+ // ---- 3. Build ApplyInput (with --rate validation + file loads) ----
122
+ const input = { consentIssued: true };
123
+ if (opts.rate !== undefined) {
124
+ if (!DECIMAL_PATTERN.test(opts.rate)) {
125
+ handleApplicationsError("jobs apply", new applications.ApplicationsError("MUTATION_ERROR", `--rate must be a non-negative decimal (got "${opts.rate}").`), opts.output);
126
+ }
127
+ input.requestedHourlyRate = opts.rate;
128
+ }
129
+ if (opts.message !== undefined)
130
+ input.message = opts.message;
131
+ // Load + parse answers / pitch JSON BEFORE any wire call (mirrors the
132
+ // #428 confirm pattern). Parse / file failures short-circuit with a
133
+ // `VALIDATION_ERROR` envelope; no mutation is issued.
134
+ if (opts.answersFile !== undefined) {
135
+ const payload = await loadJsonInputOrExit(opts.answersFile, "answers-file", opts.output);
136
+ const answers = narrowAnswersPayload(payload, opts.output);
137
+ if (answers.matcherAnswers !== undefined)
138
+ input.matcherAnswers = answers.matcherAnswers;
139
+ if (answers.expertiseAnswers !== undefined)
140
+ input.expertiseAnswers = answers.expertiseAnswers;
141
+ }
142
+ if (opts.pitchFile !== undefined) {
143
+ const payload = await loadJsonInputOrExit(opts.pitchFile, "pitch-file", opts.output);
144
+ input.pitchData = narrowPitchPayload(payload, opts.output);
145
+ }
146
+ // ---- 4. Issue apply (or dry-run preview) ----
147
+ let outcome;
148
+ try {
149
+ outcome = await applications.apply(token, id, input, { dryRun });
150
+ }
151
+ catch (err) {
152
+ handleApplicationsError("jobs apply", err, opts.output);
153
+ }
154
+ if (outcome.kind === "preview") {
155
+ // --suggest-answers + --dry-run: suppress the suggestion fetch
156
+ // (the apply mutation also issued no wire call under dry-run;
157
+ // matching that posture). The preview envelope is unchanged
158
+ // from #430.
159
+ emitDryRunSuccess({
160
+ operation: "jobs.apply",
161
+ format: opts.output,
162
+ preview: outcome.preview,
163
+ });
164
+ return;
165
+ }
166
+ // --suggest-answers (apply path): issue the suggestion fetch
167
+ // alongside the JobApply mutation result. The fetch runs AFTER
168
+ // the apply succeeded so a suggestion-fetch failure cannot block
169
+ // the apply (matches AC scenario "Suggestion query fails — apply
170
+ // continues without suggestions"). Failures emit a stderr warning
171
+ // and surface no `suggestions` section in the output.
172
+ const result = outcome.result;
173
+ const summaryParts = [`status=${result.statusV2.value}`];
174
+ if (result.requestedHourlyRate !== null)
175
+ summaryParts.push(`rate=${result.requestedHourlyRate.decimal}`);
176
+ if (opts.suggestAnswers === true) {
177
+ const suggestions = await fetchSuggestionsOrWarn(token, id);
178
+ const projection = {
179
+ application: result,
180
+ suggestions: suggestions ?? [],
181
+ };
182
+ emitUpdateSuccess({
183
+ operation: "jobs.apply",
184
+ format: opts.output,
185
+ updated: projection,
186
+ prettySummary: `Applied to job ${id}: application ${result.id} (${summaryParts.join(", ")})`,
187
+ prettyEntity: (data) => formatApplyWithSuggestions(data),
188
+ });
189
+ return;
190
+ }
191
+ emitUpdateSuccess({
192
+ operation: "jobs.apply",
193
+ format: opts.output,
194
+ updated: result,
195
+ prettySummary: `Applied to job ${id}: application ${result.id} (${summaryParts.join(", ")})`,
196
+ prettyEntity: (data) => formatJobApplicationRecord(data),
197
+ });
198
+ }
199
+ /**
200
+ * Issue `applications.similarAnswers(token, jobId)` and surface a
201
+ * stderr warning instead of crashing on failure (#452 scenario
202
+ * "Suggestion query fails — apply continues without suggestions").
203
+ * Returns `null` on failure so callers can omit the suggestions
204
+ * section from the output payload.
205
+ *
206
+ * NOT an envelope-emitting handler — failures don't terminate the
207
+ * process; the apply path continues. Used by both the
208
+ * `--show-questions` branch (preview-only) and the apply branch
209
+ * (post-apply), so the warning text is generic.
210
+ */
211
+ async function fetchSuggestionsOrWarn(token, jobId) {
212
+ try {
213
+ return await applications.similarAnswers(token, jobId);
214
+ }
215
+ catch (err) {
216
+ const msg = err instanceof Error ? err.message : String(err);
217
+ process.stderr.write(`warning: --suggest-answers fetch failed (${msg}); continuing without suggestions.\n`);
218
+ return null;
219
+ }
220
+ }
221
+ /**
222
+ * Read + parse a JSON file (or stdin) via {@link readJsonInput}. On any
223
+ * {@link JsonInputError} surface a structured `VALIDATION_ERROR` envelope
224
+ * and exit non-zero — the AC pins the envelope code, not the inner
225
+ * JsonInputError code (which appears in the message instead). Non-Error
226
+ * throws are re-raised as-is so the outer handler catches them. Returns
227
+ * the parsed value as `unknown` (Stage-1 opaque pass-through).
228
+ *
229
+ * Sibling to `loadJsonInputOrExit` in `applications/confirm.ts` —
230
+ * domain-local twin per the project's one-copy-per-CLI-surface
231
+ * convention (no cross-domain import; the two callers share the
232
+ * `JsonInputError` namespace but render `operation: "jobs.apply"` vs
233
+ * `operation: "applications.confirm"` distinctly).
234
+ */
235
+ async function loadJsonInputOrExit(rawPath, flagName, format) {
236
+ try {
237
+ return await readJsonInput(rawPath, { flagName });
238
+ }
239
+ catch (err) {
240
+ if (err instanceof JsonInputError) {
241
+ emitErrorAndExit({
242
+ operation: "jobs.apply",
243
+ format,
244
+ errors: [{ code: "VALIDATION_ERROR", message: err.message, hint: hintForJsonInputCode(err.code) }],
245
+ prettySummary: `jobs apply failed (VALIDATION_ERROR): ${err.message}`,
246
+ });
247
+ }
248
+ throw err;
249
+ }
250
+ }
251
+ /**
252
+ * Map a {@link JsonInputError} code to an actionable recovery hint
253
+ * surfaced through the `VALIDATION_ERROR` envelope's `hint:` field.
254
+ * Sibling to the same function in `applications/confirm.ts` —
255
+ * domain-local twin per the project's one-copy-per-CLI-surface
256
+ * convention.
257
+ */
258
+ function hintForJsonInputCode(code) {
259
+ switch (code) {
260
+ case "FILE_NOT_FOUND":
261
+ return "Verify the path exists; the absolute path is in the error message.";
262
+ case "FILE_READ_ERROR":
263
+ return "Check filesystem permissions on the file (and its parent directory).";
264
+ case "PARSE_ERROR":
265
+ return "Fix the JSON syntax at the cited line/column; the file shape should be { matcherAnswers: [...], expertiseAnswers: [...] }.";
266
+ case "SCHEMA_ERROR":
267
+ return "Fix the payload to match the recovered shape — matcher answers use { id, answer }; expertise answers use { questionId, other, subjectId }; pitch uses PitchInput.";
268
+ case "STDIN_UNAVAILABLE":
269
+ return "Pipe JSON into stdin (e.g. `cat answers.json | ttctl ...`) or pass a file path.";
270
+ case "STDIN_DOUBLE_CLAIM":
271
+ return "Only one of --answers-file and --pitch-file may read from stdin per invocation; pass a file path for the other.";
272
+ default:
273
+ return "Inspect the input and retry.";
274
+ }
275
+ }
276
+ /**
277
+ * Narrow the parsed JSON from `--answers-file` into the
278
+ * {@link AnswersFilePayload} shape. Surfaces a `VALIDATION_ERROR`
279
+ * envelope (NOT a wire call) when the top-level wrapper shape is wrong
280
+ * — e.g. a bare array, a string, or a non-object — OR when the inner
281
+ * arrays fail the recovered Zod schemas (per #438 Stage-2 tightening).
282
+ * Each inner schema is wrapped with `.strict()` so extra unknown keys
283
+ * surface as a field-path Zod issue (e.g. `matcherAnswers[2].questionId:
284
+ * unrecognized_keys (Unrecognized key(s) in object)`) — the AC's
285
+ * behavioral scenario 2 "Extra unknown key in payload rejected with
286
+ * field-path error".
287
+ *
288
+ * Sibling to `narrowAnswersPayload` in `applications/confirm.ts` —
289
+ * domain-local twin per the project's one-copy-per-CLI-surface
290
+ * convention. The two copies stay structurally identical; the only
291
+ * delta is the `operation:` envelope field and the `prettySummary`
292
+ * verb prefix.
293
+ */
294
+ function narrowAnswersPayload(payload, format) {
295
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
296
+ emitErrorAndExit({
297
+ operation: "jobs.apply",
298
+ format,
299
+ errors: [
300
+ {
301
+ code: "VALIDATION_ERROR",
302
+ message: "--answers-file: expected a JSON object with `matcherAnswers` and/or `expertiseAnswers` arrays.",
303
+ hint: "File shape: { matcherAnswers: [...], expertiseAnswers: [...] } — both keys are optional but the top-level value must be an object.",
304
+ },
305
+ ],
306
+ prettySummary: "jobs apply failed (VALIDATION_ERROR): --answers-file shape is not a JSON object.",
307
+ });
308
+ }
309
+ const record = payload;
310
+ const result = {};
311
+ if (record["matcherAnswers"] !== undefined) {
312
+ result.matcherAnswers = validateRecoveredOrExit(record["matcherAnswers"], MATCHER_ANSWERS_ARRAY_SCHEMA, "answers-file:matcherAnswers", format);
313
+ }
314
+ if (record["expertiseAnswers"] !== undefined) {
315
+ result.expertiseAnswers = validateRecoveredOrExit(record["expertiseAnswers"], EXPERTISE_ANSWERS_ARRAY_SCHEMA, "answers-file:expertiseAnswers", format);
316
+ }
317
+ return result;
318
+ }
319
+ /**
320
+ * Narrow the parsed JSON from `--pitch-file` into the `pitchData`
321
+ * shape. Stage-2 (#438): the inner shape is validated against the
322
+ * recovered `PitchInput` Zod schema (wrapped with `.strict()` so extra
323
+ * unknown keys reject with a field-path error). Wrong top-level shape
324
+ * (array / string / null) still surfaces as a CLI-level
325
+ * `VALIDATION_ERROR` envelope BEFORE the recovered-schema parse runs.
326
+ *
327
+ * Sibling to `narrowPitchPayload` in `applications/confirm.ts`.
328
+ */
329
+ function narrowPitchPayload(payload, format) {
330
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
331
+ emitErrorAndExit({
332
+ operation: "jobs.apply",
333
+ format,
334
+ errors: [
335
+ {
336
+ code: "VALIDATION_ERROR",
337
+ message: "--pitch-file: expected a JSON object matching the `PitchInput` shape.",
338
+ hint: "File shape: a JSON object matching the `PitchInput` schema (see `packages/core/src/__generated__/zod-schemas.ts` § PitchInputSchema).",
339
+ },
340
+ ],
341
+ prettySummary: "jobs apply failed (VALIDATION_ERROR): --pitch-file shape is not a JSON object.",
342
+ });
343
+ }
344
+ return validateRecoveredOrExit(payload, STRICT_PITCH_INPUT_SCHEMA, "pitch-file", format);
345
+ }
346
+ /**
347
+ * Wrapper around {@link parseAsRecovered} that maps the typed
348
+ * `SCHEMA_ERROR` from {@link JsonInputError} to the structured
349
+ * `VALIDATION_ERROR` envelope this command emits. Mirrors
350
+ * {@link loadJsonInputOrExit} — failure path is uniform across all
351
+ * input-validation errors (parse, syntax, schema).
352
+ */
353
+ function validateRecoveredOrExit(value, schema, flagName, format) {
354
+ try {
355
+ return parseAsRecovered(value, schema, flagName);
356
+ }
357
+ catch (err) {
358
+ if (err instanceof JsonInputError) {
359
+ emitErrorAndExit({
360
+ operation: "jobs.apply",
361
+ format,
362
+ errors: [
363
+ {
364
+ code: "VALIDATION_ERROR",
365
+ message: err.message,
366
+ hint: hintForJsonInputCode(err.code),
367
+ },
368
+ ],
369
+ prettySummary: `jobs apply failed (VALIDATION_ERROR): ${err.message}`,
370
+ });
371
+ }
372
+ throw err;
373
+ }
374
+ }
375
+ /**
376
+ * Render the post-apply `JobApplicationRecord` as the indented entity
377
+ * preview inside the success-update envelope's pretty block. Pure —
378
+ * directly unit-testable.
379
+ *
380
+ * Fields surfaced: status (value + verbose), requested rate (when
381
+ * present), the wrapping activity-item id so the user can chain
382
+ * `applications show <activity-item-id>` to view the new row.
383
+ */
384
+ export function formatJobApplicationRecord(result) {
385
+ const lines = [];
386
+ lines.push(`Application: ${result.id}`);
387
+ lines.push(`Status: ${result.statusV2.verbose} (${result.statusV2.value})`);
388
+ if (result.requestedHourlyRate !== null) {
389
+ lines.push(`Rate: ${result.requestedHourlyRate.decimal}`);
390
+ }
391
+ lines.push(`Activity item: ${result.jobActivityItemId}`);
392
+ lines.push(`(View: ttctl applications show ${result.jobActivityItemId})`);
393
+ return lines.join("\n");
394
+ }
395
+ /**
396
+ * Render the `--show-questions` projection as a sectioned multi-line
397
+ * block. Surfaces apply readiness (canApply, applyErrors,
398
+ * suggestedRate) AND the question inventories so the user can decide
399
+ * whether to apply AND author the answers-file payload in one glance.
400
+ *
401
+ * Each question renders as ` • <identifier>: <prompt>` (mirrors the
402
+ * `jobs show --with-questions` / #437 format). Empty inventories
403
+ * surface the zero count in the section header so the user reads
404
+ * "Toptal returned an empty inventory" rather than "the CLI silently
405
+ * dropped the section". Pure — directly unit-testable.
406
+ */
407
+ export function formatShowQuestions(projection) {
408
+ const lines = [];
409
+ lines.push(`Job ${projection.jobId} — Apply Preview`);
410
+ lines.push("");
411
+ lines.push(`Can apply: ${projection.canApply ? "yes" : "no"}`);
412
+ if (projection.applyErrors.length > 0) {
413
+ lines.push("Apply errors:");
414
+ for (const err of projection.applyErrors) {
415
+ lines.push(` • ${err.code}: ${err.message}`);
416
+ }
417
+ }
418
+ if (projection.suggestedRate !== null) {
419
+ lines.push(`Suggested rate: ${projection.suggestedRate}`);
420
+ }
421
+ if (projection.rateValidation !== null) {
422
+ lines.push(`Rate bounds: min=${projection.rateValidation.minRate}, step=${projection.rateValidation.rateStep.toString()}`);
423
+ }
424
+ lines.push("");
425
+ lines.push(`Matcher Questions (${projection.matcherQuestions.length.toString()})`);
426
+ for (const q of projection.matcherQuestions) {
427
+ lines.push(formatQuestionEntry(q));
428
+ }
429
+ lines.push("");
430
+ lines.push(`Expertise Questions (${projection.expertiseQuestions.length.toString()})`);
431
+ for (const q of projection.expertiseQuestions) {
432
+ lines.push(formatQuestionEntry(q));
433
+ }
434
+ // Suggestion section (#452) — only rendered when --suggest-answers
435
+ // was passed AND the fetch succeeded. Per AC scenario "Opt-in with
436
+ // past similar answers — suggestions displayed".
437
+ if (projection.suggestions !== undefined) {
438
+ lines.push("");
439
+ lines.push(`Suggestions (${projection.suggestions.length.toString()} question(s))`);
440
+ if (projection.suggestions.length === 0) {
441
+ lines.push(" (no suggestions — neither matcher nor expertise questions had similar-job history)");
442
+ }
443
+ else {
444
+ for (const group of projection.suggestions) {
445
+ lines.push(formatSuggestionGroup(group));
446
+ }
447
+ }
448
+ }
449
+ return lines.join("\n");
450
+ }
451
+ /**
452
+ * Render the apply-success envelope's pretty body when
453
+ * `--suggest-answers` was passed alongside the actual apply (#452).
454
+ * Combines the post-apply `JobApplicationRecord` section with the
455
+ * advisory suggestion inventory beneath it; the suggestions are
456
+ * displayed AFTER the apply confirmation so the user reads "apply
457
+ * succeeded" first and then sees what they could have used.
458
+ *
459
+ * Pure — directly unit-testable.
460
+ */
461
+ export function formatApplyWithSuggestions(projection) {
462
+ const lines = [];
463
+ lines.push(formatJobApplicationRecord(projection.application));
464
+ lines.push("");
465
+ lines.push(`Suggestions (${projection.suggestions.length.toString()} question(s))`);
466
+ if (projection.suggestions.length === 0) {
467
+ lines.push(" (no suggestions returned — your account may have no similar-job history)");
468
+ }
469
+ else {
470
+ for (const group of projection.suggestions) {
471
+ lines.push(formatSuggestionGroup(group));
472
+ }
473
+ }
474
+ return lines.join("\n");
475
+ }
476
+ /**
477
+ * Render one {@link applications.SimilarJobAnswerGroup} as an indented
478
+ * sub-block: the question identifier header followed by each
479
+ * historical answer as ` - "<answer>" (<createdAt>)`. Empty
480
+ * `suggestions` arrays surface the per-question header + an explicit
481
+ * `(none)` indicator so the user sees "this question was checked, no
482
+ * matches" rather than "the question vanished from the inventory".
483
+ *
484
+ * Long answer strings are NOT truncated — the talent wrote them, and
485
+ * truncating obscures the value. JSON output carries the full payload
486
+ * regardless.
487
+ */
488
+ function formatSuggestionGroup(group) {
489
+ const lines = [];
490
+ lines.push(` • ${group.questionId} (${group.suggestions.length.toString()} suggestion(s))`);
491
+ if (group.suggestions.length === 0) {
492
+ lines.push(" (none)");
493
+ }
494
+ else {
495
+ for (const s of group.suggestions) {
496
+ lines.push(` - "${s.answer}" (${s.createdAt})`);
497
+ }
498
+ }
499
+ return lines.join("\n");
500
+ }
501
+ function formatQuestionEntry(q) {
502
+ const tail = q.prompt === "" ? "" : ` ${q.prompt}`;
503
+ return ` • ${q.identifier}:${tail}`;
504
+ }
505
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../../src/commands/jobs/apply.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,14 @@
1
1
  import { Command } from "commander";
2
2
  /**
3
- * Build the `ttctl jobs` command tree (#148). Surfaces ten verbs
4
- * across the top-level group and one nested sub-group (`search`):
3
+ * Build the `ttctl jobs` command tree (#148; `apply` added in #430).
4
+ * Surfaces eleven verbs across the top-level group and one nested
5
+ * sub-group (`search`):
5
6
  *
6
7
  * | Leaf | Description |
7
8
  * |-------------------------------------------------------|--------------------------------------------|
8
9
  * | `list [filters]` | Browse current job opportunities |
9
10
  * | `show <id>` | Job detail view |
11
+ * | `apply <id> --consent [...]` | Direct-apply to a job (DESTRUCTIVE — see ADR-008) |
10
12
  * | `save <id>` | Mark a job as saved (bookmark) |
11
13
  * | `unsave <id>` | Clear interest flags (the wire's only unsave path; also clears not-interested) |
12
14
  * | `saved` | List saved jobs |
@@ -44,9 +46,21 @@ import { Command } from "commander";
44
46
  * integers). All four share `JobsListResponse` and the
45
47
  * `eligibleJobs(page, pageSize)` wire path.
46
48
  *
47
- * **Out of scope for v1** (per #148):
48
- * - Application funnel (`jobs apply` etc.) lives in `applications`.
49
- * - Bulk-save / bulk-dismiss (single-id only).
49
+ * **Application funnel write-side** (ADR-008, #430): `jobs apply <id>`
50
+ * is the user-facing direct-apply verb; the underlying service module
51
+ * is `applications.apply()` per ADR-008 § Decision Part 5 (the verb
52
+ * lives on `jobs` for readability while the funnel-crossing
53
+ * implementation lives on `applications`). The relaxation of the #15
54
+ * read-only stance is tracked in
55
+ * `hq/engineering/adr/ADR-008-application-funnel-write-side.md`.
56
+ *
57
+ * **Still out of scope** (per #148 + ADR-008 § What We're NOT Solving):
58
+ * - `JobApplication.withdraw` / `JobApplication.edit` — separate
59
+ * scope; Toptal support is uncertain.
60
+ * - Bulk-apply / bulk-save / bulk-dismiss (single-id only; matches
61
+ * the safety boundary established by #411 for IR ops).
62
+ * - Interview accept / reject — separate scope, separate catalog
63
+ * (`InterviewRejectReason`).
50
64
  */
51
65
  export declare function buildJobsCommand(): Command;
52
66
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/jobs/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAgC,MAAM,WAAW,CAAC;AAiClE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAgS1C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/jobs/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAgC,MAAM,WAAW,CAAC;AAkClE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAyX1C"}