@ttctl/cli 0.1.0-rc.7 → 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 (52) 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/reviews/index.d.ts.map +1 -1
  42. package/dist/commands/profile/reviews/index.js +5 -1
  43. package/dist/commands/profile/reviews/index.js.map +1 -1
  44. package/dist/commands/profile/reviews/submit-for-review.d.ts +8 -0
  45. package/dist/commands/profile/reviews/submit-for-review.d.ts.map +1 -1
  46. package/dist/commands/profile/reviews/submit-for-review.js +15 -1
  47. package/dist/commands/profile/reviews/submit-for-review.js.map +1 -1
  48. package/dist/lib/json-input.d.ts +121 -0
  49. package/dist/lib/json-input.d.ts.map +1 -0
  50. package/dist/lib/json-input.js +245 -0
  51. package/dist/lib/json-input.js.map +1 -0
  52. package/package.json +2 -2
@@ -0,0 +1,47 @@
1
+ import { applications } from "@ttctl/core";
2
+ import type { OutputFormat } from "../../lib/output.js";
3
+ /**
4
+ * Action handler for `ttctl applications availability-request show <id>`
5
+ * (#442). Read-only fetch of one availability request by id. The id is
6
+ * the `AvailabilityRequest.id` from `ttctl applications show
7
+ * <activityId>` output (the `Availability request: <id>` line surfaced
8
+ * when the activity row has an associated AR) — the same id the
9
+ * `applications confirm` / `reject` write-side commands take.
10
+ *
11
+ * Pretty rendering is a sectioned multi-line block grouped into
12
+ * Availability request / Comment / Job. Sections with no data (e.g. no
13
+ * recruiter comment) are omitted.
14
+ *
15
+ * `json` / `yaml` always emit the full
16
+ * {@link applications.AvailabilityRequestDetail} projection — machine
17
+ * consumers project as needed.
18
+ */
19
+ export declare function runApplicationsAvailabilityRequestShow(id: string, output: OutputFormat): Promise<void>;
20
+ /**
21
+ * Render an {@link applications.AvailabilityRequestDetail} as a
22
+ * sectioned multi-line block. Pure — directly unit-testable.
23
+ *
24
+ * Layout:
25
+ *
26
+ * Availability request <id>
27
+ * Status: <status>
28
+ * Kind: <kind>
29
+ * Fixed rate: <verbose | $<decimal>/h> // omitted if fixedRate null
30
+ * Created: <createdAt>
31
+ * Updated: <updatedAt>
32
+ * Answered: <answeredAt> // omitted if null (pending)
33
+ *
34
+ * Comment // omitted if comment null/empty
35
+ * <recruiter note, paragraph-preserved>
36
+ *
37
+ * Job // omitted if job is null
38
+ * Job id: <id>
39
+ * Title: <title>
40
+ * URL: <url>
41
+ * Client: <fullName>
42
+ *
43
+ * Per-line null guards keep the header concise when the wire returns a
44
+ * sparse availability request.
45
+ */
46
+ export declare function formatAvailabilityRequestDetail(item: applications.AvailabilityRequestDetail): string;
47
+ //# sourceMappingURL=availability-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"availability-request.d.ts","sourceRoot":"","sources":["../../../src/commands/applications/availability-request.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sCAAsC,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAa5G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,YAAY,CAAC,yBAAyB,GAAG,MAAM,CA+BpG"}
@@ -0,0 +1,98 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { applications } from "@ttctl/core";
4
+ import { emitResult } from "../../lib/output.js";
5
+ import { formatFixedRate, handleApplicationsError, loadAuthTokenOrExit } from "./shared.js";
6
+ /**
7
+ * Action handler for `ttctl applications availability-request show <id>`
8
+ * (#442). Read-only fetch of one availability request by id. The id is
9
+ * the `AvailabilityRequest.id` from `ttctl applications show
10
+ * <activityId>` output (the `Availability request: <id>` line surfaced
11
+ * when the activity row has an associated AR) — the same id the
12
+ * `applications confirm` / `reject` write-side commands take.
13
+ *
14
+ * Pretty rendering is a sectioned multi-line block grouped into
15
+ * Availability request / Comment / Job. Sections with no data (e.g. no
16
+ * recruiter comment) are omitted.
17
+ *
18
+ * `json` / `yaml` always emit the full
19
+ * {@link applications.AvailabilityRequestDetail} projection — machine
20
+ * consumers project as needed.
21
+ */
22
+ export async function runApplicationsAvailabilityRequestShow(id, output) {
23
+ const token = await loadAuthTokenOrExit("applications availability-request show", output);
24
+ let item;
25
+ try {
26
+ item = await applications.availabilityRequests.show(token, id);
27
+ }
28
+ catch (err) {
29
+ handleApplicationsError("applications availability-request show", err, output);
30
+ }
31
+ emitResult(item, output, {
32
+ pretty: (data) => formatAvailabilityRequestDetail(data),
33
+ });
34
+ }
35
+ /**
36
+ * Render an {@link applications.AvailabilityRequestDetail} as a
37
+ * sectioned multi-line block. Pure — directly unit-testable.
38
+ *
39
+ * Layout:
40
+ *
41
+ * Availability request <id>
42
+ * Status: <status>
43
+ * Kind: <kind>
44
+ * Fixed rate: <verbose | $<decimal>/h> // omitted if fixedRate null
45
+ * Created: <createdAt>
46
+ * Updated: <updatedAt>
47
+ * Answered: <answeredAt> // omitted if null (pending)
48
+ *
49
+ * Comment // omitted if comment null/empty
50
+ * <recruiter note, paragraph-preserved>
51
+ *
52
+ * Job // omitted if job is null
53
+ * Job id: <id>
54
+ * Title: <title>
55
+ * URL: <url>
56
+ * Client: <fullName>
57
+ *
58
+ * Per-line null guards keep the header concise when the wire returns a
59
+ * sparse availability request.
60
+ */
61
+ export function formatAvailabilityRequestDetail(item) {
62
+ const lines = [];
63
+ lines.push(`Availability request ${item.id}`);
64
+ if (item.status !== null)
65
+ lines.push(` Status: ${item.status}`);
66
+ if (item.kind !== null)
67
+ lines.push(` Kind: ${item.kind}`);
68
+ if (item.fixedRate !== null)
69
+ lines.push(` Fixed rate: ${formatFixedRate(item.fixedRate)}`);
70
+ if (item.createdAt !== null)
71
+ lines.push(` Created: ${item.createdAt}`);
72
+ if (item.updatedAt !== null)
73
+ lines.push(` Updated: ${item.updatedAt}`);
74
+ if (item.answeredAt !== null)
75
+ lines.push(` Answered: ${item.answeredAt}`);
76
+ if (item.comment !== null && item.comment !== "") {
77
+ lines.push("");
78
+ lines.push("Comment");
79
+ for (const para of item.comment.split(/\n+/)) {
80
+ if (para.trim().length > 0)
81
+ lines.push(` ${para}`);
82
+ }
83
+ }
84
+ if (item.job !== null) {
85
+ lines.push("");
86
+ lines.push("Job");
87
+ lines.push(` Job id: ${item.job.id}`);
88
+ if (item.job.title !== null && item.job.title !== "")
89
+ lines.push(` Title: ${item.job.title}`);
90
+ if (item.job.url !== null && item.job.url !== "")
91
+ lines.push(` URL: ${item.job.url}`);
92
+ if (item.job.client?.fullName != null && item.job.client.fullName !== "") {
93
+ lines.push(` Client: ${item.job.client.fullName}`);
94
+ }
95
+ }
96
+ return lines.join("\n");
97
+ }
98
+ //# sourceMappingURL=availability-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"availability-request.js","sourceRoot":"","sources":["../../../src/commands/applications/availability-request.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,36 @@
1
1
  import { applications } from "@ttctl/core";
2
2
  import type { OutputFormat } from "../../lib/output.js";
3
3
  /**
4
- * Options for `ttctl applications confirm <id>` (#411). Mirrors the
5
- * service-layer {@link applications.ConfirmInput} shape with CLI-side
4
+ * Options for `ttctl applications confirm <id>` (#411 + #428). Mirrors
5
+ * the service-layer {@link applications.ConfirmInput} shape with CLI-side
6
6
  * argument validation.
7
+ *
8
+ * `answersFile` / `pitchFile` (#428) are CLI-only paths (or `-` for
9
+ * stdin) — the action handler reads + parses the JSON file BEFORE calling
10
+ * into the service, then forwards the parsed payloads as
11
+ * `matcherQuestionsAnswers` / `expertiseQuestionsAnswers` / `pitchInput`
12
+ * (the Stage-1 opaque pass-through; see ADR-008 § Decision Part 3).
7
13
  */
8
14
  export interface ApplicationsConfirmOptions {
9
15
  message?: string;
10
16
  rate?: string;
11
17
  kind?: applications.AvailabilityRequestKind;
18
+ /**
19
+ * Path to a JSON file (or `-` for stdin) containing
20
+ * `{ matcherAnswers: [...], expertiseAnswers: [...] }` per ADR-008
21
+ * § Decision Part 2. Parsed payload forwarded as
22
+ * `matcherQuestionsAnswers` / `expertiseQuestionsAnswers` to the
23
+ * service. Question identifiers come from `applications show
24
+ * <activityId>` output.
25
+ */
26
+ answersFile?: string;
27
+ /**
28
+ * Path to a JSON file (or `-` for stdin) containing a `PitchInput`
29
+ * object. Parsed payload forwarded as `pitchInput` (note: core field
30
+ * is `pitchInput`; wire variable is `$pitchInput` mapping to the
31
+ * mutation's `pitchData` input field — see ADR-008 § Decision Part 2).
32
+ */
33
+ pitchFile?: string;
12
34
  output: OutputFormat;
13
35
  }
14
36
  /**
@@ -29,6 +51,14 @@ export interface ApplicationsConfirmOptions {
29
51
  * Flexible-kind ARs have no recruiter-pinned rate, so callers MUST
30
52
  * pass `--rate <decimal>` when the AR is FLEXIBLE / MARKETPLACE_FLEXIBLE.
31
53
  *
54
+ * Optional `--answers-file <path>` / `--pitch-file <path>` (#428) — see
55
+ * the option type {@link ApplicationsConfirmOptions} for the file
56
+ * grammar. Both flags accept `-` to read JSON from stdin per commander
57
+ * convention; per ADR-008 § Decision Part 2 only one flag may claim
58
+ * stdin per invocation. Files are read + parsed BEFORE any wire call;
59
+ * malformed JSON refuses with the `VALIDATION_ERROR` envelope and no
60
+ * mutation is issued.
61
+ *
32
62
  * **DESTRUCTIVE** — confirming an IR transitions the AR to
33
63
  * `AVAILABILITY_REQUEST_CONFIRMED` and creates a `JobApplication`. No
34
64
  * withdraw operation is available on the wire. Prefer `--dry-run` to
@@ -1 +1 @@
1
- {"version":3,"file":"confirm.d.ts","sourceRoot":"","sources":["../../../src/commands/applications/confirm.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,YAAY,CAAC,uBAAuB,CAAC;IAC5C,MAAM,EAAE,YAAY,CAAC;CACtB;AAID;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAiDxG;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,CAAC,iCAAiC,GAAG,MAAM,CAcnG"}
1
+ {"version":3,"file":"confirm.d.ts","sourceRoot":"","sources":["../../../src/commands/applications/confirm.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAM3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,YAAY,CAAC,uBAAuB,CAAC;IAC5C;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;CACtB;AA+CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAkExG;AA8JD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,CAAC,iCAAiC,GAAG,MAAM,CAcnG"}
@@ -1,10 +1,34 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-only
2
2
  // Copyright (C) 2026 Oleksii PELYKH
3
3
  import { applications } from "@ttctl/core";
4
+ import { z } from "zod";
4
5
  import { getCliDryRun } from "../../lib/dry-run.js";
5
- import { emitDryRunSuccess, emitUpdateSuccess } from "../../lib/envelopes.js";
6
+ import { emitDryRunSuccess, emitErrorAndExit, emitUpdateSuccess } from "../../lib/envelopes.js";
7
+ import { JsonInputError, parseAsRecovered, readJsonInput } from "../../lib/json-input.js";
6
8
  import { handleApplicationsError, loadAuthTokenOrExit } from "./shared.js";
7
9
  const DECIMAL_PATTERN = /^\d+(\.\d+)?$/;
10
+ /**
11
+ * Stage-2 Zod schemas for `--answers-file` payload validation (#438).
12
+ *
13
+ * Built ONCE at module load — the factory pattern in
14
+ * `__generated__/zod-schemas.ts` returns a fresh `z.object(...)` per
15
+ * call; we materialize each schema once and reuse it across parse
16
+ * invocations.
17
+ *
18
+ * `.strict()` is mandatory at this boundary: codegen emits schemas with
19
+ * default "strip unknown" semantics, which would silently pass extra
20
+ * keys. The AC requires "extra unknown key in payload rejected with
21
+ * field-path error" → wrap each inner schema with `.strict()`.
22
+ *
23
+ * Array wrappers (`z.array(...)`) live at the call site so each element
24
+ * is validated independently; per-entry failures surface their array
25
+ * index in the field path (`matcherAnswers[2].id: …`).
26
+ */
27
+ const STRICT_MATCHER_ANSWER_SCHEMA = applications.JobPositionAnswerInputSchema().strict();
28
+ const STRICT_EXPERTISE_ANSWER_SCHEMA = applications.JobExpertiseAnswerInputSchema().strict();
29
+ const STRICT_PITCH_INPUT_SCHEMA = applications.PitchInputSchema().strict();
30
+ const MATCHER_ANSWERS_ARRAY_SCHEMA = z.array(STRICT_MATCHER_ANSWER_SCHEMA);
31
+ const EXPERTISE_ANSWERS_ARRAY_SCHEMA = z.array(STRICT_EXPERTISE_ANSWER_SCHEMA);
8
32
  /**
9
33
  * Action handler for `ttctl applications confirm <id>` (#411).
10
34
  *
@@ -23,6 +47,14 @@ const DECIMAL_PATTERN = /^\d+(\.\d+)?$/;
23
47
  * Flexible-kind ARs have no recruiter-pinned rate, so callers MUST
24
48
  * pass `--rate <decimal>` when the AR is FLEXIBLE / MARKETPLACE_FLEXIBLE.
25
49
  *
50
+ * Optional `--answers-file <path>` / `--pitch-file <path>` (#428) — see
51
+ * the option type {@link ApplicationsConfirmOptions} for the file
52
+ * grammar. Both flags accept `-` to read JSON from stdin per commander
53
+ * convention; per ADR-008 § Decision Part 2 only one flag may claim
54
+ * stdin per invocation. Files are read + parsed BEFORE any wire call;
55
+ * malformed JSON refuses with the `VALIDATION_ERROR` envelope and no
56
+ * mutation is issued.
57
+ *
26
58
  * **DESTRUCTIVE** — confirming an IR transitions the AR to
27
59
  * `AVAILABILITY_REQUEST_CONFIRMED` and creates a `JobApplication`. No
28
60
  * withdraw operation is available on the wire. Prefer `--dry-run` to
@@ -42,6 +74,24 @@ export async function runApplicationsConfirm(id, opts) {
42
74
  }
43
75
  if (opts.kind !== undefined)
44
76
  input.kind = opts.kind;
77
+ // Load + parse answers / pitch JSON BEFORE any wire call (#428). Any
78
+ // parse / file failure short-circuits with a `VALIDATION_ERROR`
79
+ // envelope; no mutation is issued. Both flags surface their typed
80
+ // `JsonInputError` codes through the SAME `VALIDATION_ERROR` envelope
81
+ // for consistency (the AC pins the envelope code, not the inner
82
+ // JsonInputError code).
83
+ if (opts.answersFile !== undefined) {
84
+ const payload = await loadJsonInputOrExit(opts.answersFile, "answers-file", opts.output);
85
+ const answers = narrowAnswersPayload(payload, opts.output);
86
+ if (answers.matcherAnswers !== undefined)
87
+ input.matcherQuestionsAnswers = answers.matcherAnswers;
88
+ if (answers.expertiseAnswers !== undefined)
89
+ input.expertiseQuestionsAnswers = answers.expertiseAnswers;
90
+ }
91
+ if (opts.pitchFile !== undefined) {
92
+ const payload = await loadJsonInputOrExit(opts.pitchFile, "pitch-file", opts.output);
93
+ input.pitchInput = narrowPitchPayload(payload, opts.output);
94
+ }
45
95
  let outcome;
46
96
  try {
47
97
  outcome = await applications.confirm(token, id, input, { dryRun });
@@ -71,6 +121,149 @@ export async function runApplicationsConfirm(id, opts) {
71
121
  prettyEntity: (data) => formatRespondPayload(data),
72
122
  });
73
123
  }
124
+ /**
125
+ * Read + parse a JSON file (or stdin) via {@link readJsonInput}. On any
126
+ * {@link JsonInputError} surface a structured `VALIDATION_ERROR` envelope
127
+ * and exit non-zero — the AC pins the envelope code, not the inner
128
+ * JsonInputError code (which appears in the message instead). Non-Error
129
+ * throws are re-raised as-is so the outer handler catches them. Returns
130
+ * the parsed value as `unknown` (Stage-1 opaque pass-through).
131
+ */
132
+ async function loadJsonInputOrExit(rawPath, flagName, format) {
133
+ try {
134
+ return await readJsonInput(rawPath, { flagName });
135
+ }
136
+ catch (err) {
137
+ if (err instanceof JsonInputError) {
138
+ emitErrorAndExit({
139
+ operation: "applications.confirm",
140
+ format,
141
+ errors: [{ code: "VALIDATION_ERROR", message: err.message, hint: hintForJsonInputCode(err.code) }],
142
+ prettySummary: `applications confirm failed (VALIDATION_ERROR): ${err.message}`,
143
+ });
144
+ }
145
+ throw err;
146
+ }
147
+ }
148
+ /**
149
+ * Map a {@link JsonInputError} code to an actionable recovery hint
150
+ * surfaced through the `VALIDATION_ERROR` envelope's `hint:` field. The
151
+ * AC mandates "Recovery hint cites the parse failure line/column" for
152
+ * `PARSE_ERROR` — the line/column live in the `message` (formatted by
153
+ * {@link readJsonInput}), so the hint here surfaces the procedural
154
+ * recovery action.
155
+ */
156
+ function hintForJsonInputCode(code) {
157
+ switch (code) {
158
+ case "FILE_NOT_FOUND":
159
+ return "Verify the path exists; the absolute path is in the error message.";
160
+ case "FILE_READ_ERROR":
161
+ return "Check filesystem permissions on the file (and its parent directory).";
162
+ case "PARSE_ERROR":
163
+ return "Fix the JSON syntax at the cited line/column; the file shape should be { matcherAnswers: [...], expertiseAnswers: [...] }.";
164
+ case "SCHEMA_ERROR":
165
+ return "Fix the payload to match the recovered shape — matcher answers use { id, answer }; expertise answers use { questionId, other, subjectId }; pitch uses PitchInput.";
166
+ case "STDIN_UNAVAILABLE":
167
+ return "Pipe JSON into stdin (e.g. `cat answers.json | ttctl ...`) or pass a file path.";
168
+ case "STDIN_DOUBLE_CLAIM":
169
+ return "Only one of --answers-file and --pitch-file may read from stdin per invocation; pass a file path for the other.";
170
+ default:
171
+ return "Inspect the input and retry.";
172
+ }
173
+ }
174
+ /**
175
+ * Narrow the parsed JSON from `--answers-file` into the
176
+ * {@link AnswersFilePayload} shape. Surfaces a `VALIDATION_ERROR`
177
+ * envelope (NOT a wire call) when the top-level wrapper shape is wrong
178
+ * — e.g. a bare array, a string, or a non-object — OR when the inner
179
+ * arrays fail the recovered Zod schemas (per #438 Stage-2 tightening).
180
+ * Each inner schema is wrapped with `.strict()` so extra unknown keys
181
+ * surface as a field-path Zod issue (e.g. `matcherAnswers[2].questionId:
182
+ * unrecognized_keys (Unrecognized key(s) in object)`) — the AC's
183
+ * behavioral scenario 2 "Extra unknown key in payload rejected with
184
+ * field-path error".
185
+ */
186
+ function narrowAnswersPayload(payload, format) {
187
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
188
+ emitErrorAndExit({
189
+ operation: "applications.confirm",
190
+ format,
191
+ errors: [
192
+ {
193
+ code: "VALIDATION_ERROR",
194
+ message: "--answers-file: expected a JSON object with `matcherAnswers` and/or `expertiseAnswers` arrays.",
195
+ hint: "File shape: { matcherAnswers: [...], expertiseAnswers: [...] } — both keys are optional but the top-level value must be an object.",
196
+ },
197
+ ],
198
+ prettySummary: "applications confirm failed (VALIDATION_ERROR): --answers-file shape is not a JSON object.",
199
+ });
200
+ }
201
+ const record = payload;
202
+ const result = {};
203
+ if (record["matcherAnswers"] !== undefined) {
204
+ result.matcherAnswers = validateRecoveredOrExit(record["matcherAnswers"], MATCHER_ANSWERS_ARRAY_SCHEMA, "answers-file:matcherAnswers", format);
205
+ }
206
+ if (record["expertiseAnswers"] !== undefined) {
207
+ result.expertiseAnswers = validateRecoveredOrExit(record["expertiseAnswers"], EXPERTISE_ANSWERS_ARRAY_SCHEMA, "answers-file:expertiseAnswers", format);
208
+ }
209
+ return result;
210
+ }
211
+ /**
212
+ * Narrow the parsed JSON from `--pitch-file` into the `pitchInput`
213
+ * record shape. Stage-2 (#438): the inner shape is validated against
214
+ * the recovered `PitchInput` Zod schema (wrapped with `.strict()` so
215
+ * extra unknown keys reject with a field-path error). Wrong top-level
216
+ * shape (array / string / null) still surfaces as a CLI-level
217
+ * `VALIDATION_ERROR` envelope BEFORE the recovered-schema parse runs —
218
+ * the JSON-object precondition is a precondition of the Zod schema's
219
+ * `z.object(...)` semantics.
220
+ */
221
+ function narrowPitchPayload(payload, format) {
222
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
223
+ emitErrorAndExit({
224
+ operation: "applications.confirm",
225
+ format,
226
+ errors: [
227
+ {
228
+ code: "VALIDATION_ERROR",
229
+ message: "--pitch-file: expected a JSON object matching the `PitchInput` shape.",
230
+ hint: "File shape: a JSON object matching the `PitchInput` schema (see `packages/core/src/__generated__/zod-schemas.ts` § PitchInputSchema).",
231
+ },
232
+ ],
233
+ prettySummary: "applications confirm failed (VALIDATION_ERROR): --pitch-file shape is not a JSON object.",
234
+ });
235
+ }
236
+ return validateRecoveredOrExit(payload, STRICT_PITCH_INPUT_SCHEMA, "pitch-file", format);
237
+ }
238
+ /**
239
+ * Wrapper around {@link parseAsRecovered} that maps the typed
240
+ * `SCHEMA_ERROR` from {@link JsonInputError} to the structured
241
+ * `VALIDATION_ERROR` envelope this command emits. Mirrors
242
+ * {@link loadJsonInputOrExit} — failure path is uniform across all
243
+ * input-validation errors (parse, syntax, schema).
244
+ */
245
+ function validateRecoveredOrExit(value, schema, flagName, format) {
246
+ try {
247
+ return parseAsRecovered(value, schema, flagName);
248
+ }
249
+ catch (err) {
250
+ if (err instanceof JsonInputError) {
251
+ emitErrorAndExit({
252
+ operation: "applications.confirm",
253
+ format,
254
+ errors: [
255
+ {
256
+ code: "VALIDATION_ERROR",
257
+ message: err.message,
258
+ hint: hintForJsonInputCode(err.code),
259
+ },
260
+ ],
261
+ prettySummary: `applications confirm failed (VALIDATION_ERROR): ${err.message}`,
262
+ });
263
+ }
264
+ throw err;
265
+ }
266
+ }
74
267
  /**
75
268
  * Render the post-confirm/reject AR projection as the indented entity
76
269
  * preview inside the success-update envelope's pretty block. Shared
@@ -1 +1 @@
1
- {"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../../src/commands/applications/confirm.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../../src/commands/applications/confirm.ts"],"names":[],"mappings":"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"}
@@ -10,8 +10,12 @@ import { Command } from "commander";
10
10
  * | `show <id>` | Detail view for one row |
11
11
  * | `stats` | Per-status-group counts (5 server calls in parallel) |
12
12
  *
13
- * Per project non-goals (#15): no apply / withdraw / edit operations
14
- * are exposed. The CLI is read-only by design.
13
+ * Write operations on the application funnel are in scope per ADR-008
14
+ * (ttctl) `hq/engineering/adr/ADR-008-application-funnel-write-side.md`.
15
+ * ADR-008 § Decision relaxes the #15 read-only non-goal and bounds the
16
+ * write surface: Interest Request confirm / reject (shipped in #411)
17
+ * and direct job application are in scope; `withdraw` / `edit`, bulk
18
+ * apply, and interview accept / reject remain explicitly out of scope.
15
19
  *
16
20
  * **Pagination (#377)**: the `list` leaf declares `--page` /
17
21
  * `--per-page` (1-indexed positive integers; same `parsePaginationFlag`
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/applications/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAgC,MAAM,WAAW,CAAC;AA+BlE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CA8IlD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/applications/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAgC,MAAM,WAAW,CAAC;AAqClE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAoRlD"}
@@ -4,7 +4,9 @@ import { Command, InvalidArgumentError, Option } from "commander";
4
4
  import { applications } from "@ttctl/core";
5
5
  import { OUTPUT_FORMATS } from "../../lib/output.js";
6
6
  import { parsePaginationFlag } from "../../lib/pagination.js";
7
+ import { runApplicationsAvailabilityRequestShow } from "./availability-request.js";
7
8
  import { runApplicationsConfirm } from "./confirm.js";
9
+ import { runApplicationsInterviewGuideShow, runApplicationsInterviewNotesShow, runApplicationsInterviewShow, } from "./interview.js";
8
10
  import { runApplicationsList } from "./list.js";
9
11
  import { runApplicationsReject } from "./reject.js";
10
12
  import { runApplicationsRejectReasons } from "./reject-reasons.js";
@@ -34,8 +36,12 @@ function perPageOption() {
34
36
  * | `show <id>` | Detail view for one row |
35
37
  * | `stats` | Per-status-group counts (5 server calls in parallel) |
36
38
  *
37
- * Per project non-goals (#15): no apply / withdraw / edit operations
38
- * are exposed. The CLI is read-only by design.
39
+ * Write operations on the application funnel are in scope per ADR-008
40
+ * (ttctl) `hq/engineering/adr/ADR-008-application-funnel-write-side.md`.
41
+ * ADR-008 § Decision relaxes the #15 read-only non-goal and bounds the
42
+ * write surface: Interest Request confirm / reject (shipped in #411)
43
+ * and direct job application are in scope; `withdraw` / `edit`, bulk
44
+ * apply, and interview accept / reject remain explicitly out of scope.
39
45
  *
40
46
  * **Pagination (#377)**: the `list` leaf declares `--page` /
41
47
  * `--per-page` (1-indexed positive integers; same `parsePaginationFlag`
@@ -101,15 +107,29 @@ export function buildApplicationsCommand() {
101
107
  // the AvailabilityRequest id (NOT the activity-item id) — discover
102
108
  // it via `applications show <activityId>` (look for the "Availability
103
109
  // request: <id>" line).
110
+ // #428 — answers-file / pitch-file help text. ADR-008 § Decision
111
+ // Part 2 locks the JSON-file grammar; per the issue's AC the help
112
+ // must document the JSON shape (5-line example), the question-id
113
+ // discovery hint, and the stdin escape.
114
+ const ANSWERS_FILE_HELP = "JSON file (or `-` for stdin) containing matcher/expertise answers. Shape:\n" +
115
+ " {\n" +
116
+ ' "matcherAnswers": [{"id": "<questionIdentifier>", "answer": "<value>"}],\n' +
117
+ ' "expertiseAnswers": [{"questionId": "<questionIdentifier>", "other": null, "subjectId": null}]\n' +
118
+ " }\n" +
119
+ "Per the recovered SDL (#438): matcher answers carry the id at `id`; expertise answers carry it at `questionId`. " +
120
+ "Question identifiers come from `applications show <activityId>` output. Stdin escape: `--answers-file -`.";
121
+ const PITCH_FILE_HELP = "JSON file (or `-` for stdin) containing the PitchInput payload (single JSON object matching the recovered SDL shape). Stdin escape: `--pitch-file -`.";
104
122
  cmd
105
123
  .command("confirm")
106
- .description("Confirm an Interest Request (DESTRUCTIVE — creates a JobApplication; no undo)")
124
+ .description("Confirm an Interest Request (DESTRUCTIVE — creates a JobApplication; no undo). See `Interest Requests` in the README for the full workflow.")
107
125
  .argument("<id>", "AvailabilityRequest id (NOT the activity-item id)", parseIdArg)
108
126
  .option("-m, --message <text>", "optional talent free-text accompanying the confirmation")
109
127
  .option("--rate <decimal>", "requested hourly rate (decimal string, e.g. 80.00). Auto-filled from the AR's Fixed rate when omitted; required for FLEXIBLE / MARKETPLACE_FLEXIBLE ARs")
110
128
  .addOption(new Option("--kind <kind>", "AR kind (auto-detected from metadata when omitted)").choices([
111
129
  ...applications.AVAILABILITY_REQUEST_KINDS,
112
130
  ]))
131
+ .option("--answers-file <path>", ANSWERS_FILE_HELP)
132
+ .option("--pitch-file <path>", PITCH_FILE_HELP)
113
133
  .addOption(new Option("-o, --output <format>", "output format")
114
134
  .choices(OUTPUT_FORMATS)
115
135
  .default("pretty"))
@@ -121,6 +141,10 @@ export function buildApplicationsCommand() {
121
141
  runOpts.rate = options.rate;
122
142
  if (options.kind !== undefined)
123
143
  runOpts.kind = options.kind;
144
+ if (options.answersFile !== undefined)
145
+ runOpts.answersFile = options.answersFile;
146
+ if (options.pitchFile !== undefined)
147
+ runOpts.pitchFile = options.pitchFile;
124
148
  await runApplicationsConfirm(id, runOpts);
125
149
  });
126
150
  cmd
@@ -150,6 +174,91 @@ export function buildApplicationsCommand() {
150
174
  .action(async (options) => {
151
175
  await runApplicationsRejectReasons(options.output);
152
176
  });
177
+ // #439 — Interview detail. Sibling sub-namespace `interview show <id>`
178
+ // for the rich `TalentInterview` projection (interviewer, scheduled
179
+ // slot, agenda link, prep-guide ref). The id comes from
180
+ // `applications show <activityId>` output (the `Interview: <id>`
181
+ // line). Read-only — no confirm / reject verbs (those remain
182
+ // out-of-scope per ADR-008 § Decision).
183
+ const interviewCmd = cmd
184
+ .command("interview")
185
+ .description("Interview detail (read-only). See `applications show <activityId>` for the id.");
186
+ interviewCmd
187
+ .command("show")
188
+ .description("Show one interview by id")
189
+ .argument("<id>", "id of the interview to show", parseIdArg)
190
+ .addOption(new Option("-o, --output <format>", "output format")
191
+ .choices(OUTPUT_FORMATS)
192
+ .default("pretty"))
193
+ .action(async (id, options) => {
194
+ await runApplicationsInterviewShow(id, options.output);
195
+ });
196
+ // #440 — Interview notes (portal-side `GetInterviewNotes`). Sub-sub-
197
+ // namespace `interview notes show <jobId>` for the talent's prep
198
+ // notes attached to an interview. Read-only.
199
+ //
200
+ // **Input is the JOB id, not the interview id** — the wire op takes
201
+ // `$jobId: ID!` and traverses
202
+ // `viewer.job(id).activityItem.interview.{id, kind, talentNotes}`.
203
+ // Discover the job id via `applications interview show <interviewId>`
204
+ // (the `Job → Job id` line) or `applications show <activityId>`.
205
+ const notesCmd = interviewCmd
206
+ .command("notes")
207
+ .description("Interview prep notes (read-only). Sub-sub-namespace of `interview`.");
208
+ notesCmd
209
+ .command("show")
210
+ .description("Read the talent's prep notes for the interview attached to a job (input is the JOB id, NOT the interview id)")
211
+ .argument("<jobId>", "TalentJob id (discover via `applications interview show <interviewId>` → `Job → Job id`)", parseIdArg)
212
+ .addOption(new Option("-o, --output <format>", "output format")
213
+ .choices(OUTPUT_FORMATS)
214
+ .default("pretty"))
215
+ .action(async (jobId, options) => {
216
+ await runApplicationsInterviewNotesShow(jobId, options.output);
217
+ });
218
+ // #470 — Interview prep guide (mobile-gateway `InterviewGuide`).
219
+ // Sub-sub-namespace `interview guide show <interviewId>` for the
220
+ // interview-prep guide content (sections + tips) attached to one
221
+ // interview. Read-only.
222
+ //
223
+ // Sibling of `notes` — both deepen the `interview` namespace. Input
224
+ // is the INTERVIEW id (the wire op takes `$interviewId: ID!`).
225
+ // The `Prep guide → ID: <guideId>` line surfaced by
226
+ // `applications interview show` is the back-pointer; the guide
227
+ // CONTENT lives behind this leaf.
228
+ const guideCmd = interviewCmd
229
+ .command("guide")
230
+ .description("Interview prep guide (sections + tips). Sub-sub-namespace of `interview`.");
231
+ guideCmd
232
+ .command("show")
233
+ .description("Read the interview-prep guide content (sections + tips) for one interview")
234
+ .argument("<interviewId>", "Interview id (discover via `applications interview show <interviewId>` or `applications show <activityId>`)", parseIdArg)
235
+ .addOption(new Option("-o, --output <format>", "output format")
236
+ .choices(OUTPUT_FORMATS)
237
+ .default("pretty"))
238
+ .action(async (interviewId, options) => {
239
+ await runApplicationsInterviewGuideShow(interviewId, options.output);
240
+ });
241
+ // #442 — Availability-request detail. Sibling read-only sub-namespace
242
+ // `availability-request show <id>` for the rich `AvailabilityRequest`
243
+ // projection (status, kind, recruiter-pinned Fixed rate, recruiter
244
+ // comment, lifecycle timestamps, job). The `<id>` is the
245
+ // `AvailabilityRequest.id` from `applications show <activityId>`
246
+ // output (the `Availability request: <id>` line) — the SAME id the
247
+ // `confirm` / `reject` write-side leaves accept, NOT the activity-item
248
+ // id. Read-only — the confirm / reject verbs are the write surface.
249
+ const availabilityRequestCmd = cmd
250
+ .command("availability-request")
251
+ .description("Availability-request detail (read-only). See `applications show <activityId>` for the id.");
252
+ availabilityRequestCmd
253
+ .command("show")
254
+ .description("Show one availability request by id")
255
+ .argument("<id>", "AvailabilityRequest id (NOT the activity-item id)", parseIdArg)
256
+ .addOption(new Option("-o, --output <format>", "output format")
257
+ .choices(OUTPUT_FORMATS)
258
+ .default("pretty"))
259
+ .action(async (id, options) => {
260
+ await runApplicationsAvailabilityRequestShow(id, options.output);
261
+ });
153
262
  return cmd;
154
263
  }
155
264
  /**