@bussolabs/closeyourit-cli 0.19.0 → 0.21.0

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 (82) hide show
  1. package/README.md +39 -5
  2. package/dist/base.d.ts +22 -0
  3. package/dist/base.js +46 -0
  4. package/dist/commands/alerts/notifications/read-all.js +3 -0
  5. package/dist/commands/alerts/preferences/show.js +2 -0
  6. package/dist/commands/ideas/convert.d.ts +2 -2
  7. package/dist/commands/ideas/convert.js +25 -8
  8. package/dist/commands/kb/book/add-page.d.ts +18 -0
  9. package/dist/commands/kb/book/add-page.js +55 -0
  10. package/dist/commands/kb/book/list.d.ts +12 -0
  11. package/dist/commands/kb/book/list.js +33 -0
  12. package/dist/commands/kb/book/show.d.ts +9 -0
  13. package/dist/commands/kb/book/show.js +22 -0
  14. package/dist/commands/kb/create.js +29 -6
  15. package/dist/commands/kb/publish.d.ts +15 -0
  16. package/dist/commands/kb/publish.js +106 -0
  17. package/dist/commands/kb/update.js +26 -5
  18. package/dist/commands/logout.js +3 -0
  19. package/dist/commands/org/show.js +2 -0
  20. package/dist/commands/personal/import.js +2 -2
  21. package/dist/commands/personal/run.js +1 -1
  22. package/dist/commands/run.js +1 -1
  23. package/dist/commands/secrets/import.js +2 -2
  24. package/dist/commands/seo/ignore.d.ts +12 -0
  25. package/dist/commands/seo/ignore.js +27 -0
  26. package/dist/commands/seo/list.d.ts +13 -0
  27. package/dist/commands/seo/list.js +52 -0
  28. package/dist/commands/seo/pages.d.ts +11 -0
  29. package/dist/commands/seo/pages.js +44 -0
  30. package/dist/commands/seo/promote.d.ts +9 -0
  31. package/dist/commands/seo/promote.js +22 -0
  32. package/dist/commands/seo/reopen.d.ts +9 -0
  33. package/dist/commands/seo/reopen.js +21 -0
  34. package/dist/commands/seo/rescan.d.ts +9 -0
  35. package/dist/commands/seo/rescan.js +21 -0
  36. package/dist/commands/seo/show.d.ts +9 -0
  37. package/dist/commands/seo/show.js +35 -0
  38. package/dist/commands/seo-sites/create.d.ts +14 -0
  39. package/dist/commands/seo-sites/create.js +43 -0
  40. package/dist/commands/seo-sites/delete.d.ts +9 -0
  41. package/dist/commands/seo-sites/delete.js +21 -0
  42. package/dist/commands/seo-sites/list.d.ts +10 -0
  43. package/dist/commands/seo-sites/list.js +38 -0
  44. package/dist/commands/seo-sites/update.d.ts +17 -0
  45. package/dist/commands/seo-sites/update.js +45 -0
  46. package/dist/commands/tickets/comment.d.ts +14 -3
  47. package/dist/commands/tickets/comment.js +29 -1
  48. package/dist/commands/tickets/create.d.ts +6 -6
  49. package/dist/commands/tickets/create.js +21 -2
  50. package/dist/commands/tickets/report-show.d.ts +20 -0
  51. package/dist/commands/tickets/report-show.js +38 -0
  52. package/dist/commands/tickets/report-versions.d.ts +19 -0
  53. package/dist/commands/tickets/report-versions.js +40 -0
  54. package/dist/commands/tickets/report.d.ts +24 -0
  55. package/dist/commands/tickets/report.js +69 -0
  56. package/dist/commands/tickets/status.d.ts +1 -1
  57. package/dist/commands/tickets/status.js +14 -4
  58. package/dist/commands/tickets/update.d.ts +25 -6
  59. package/dist/commands/tickets/update.js +144 -9
  60. package/dist/commands/whoami.js +3 -0
  61. package/dist/errors/error-codes.d.ts +12 -0
  62. package/dist/errors/error-codes.js +19 -0
  63. package/dist/errors/input-error.d.ts +16 -0
  64. package/dist/errors/input-error.js +42 -0
  65. package/dist/lib/alert-events.js +1 -0
  66. package/dist/lib/knowledge.d.ts +24 -0
  67. package/dist/lib/knowledge.js +68 -0
  68. package/dist/lib/limits.d.ts +59 -0
  69. package/dist/lib/limits.js +92 -0
  70. package/dist/lib/stdin.d.ts +1 -2
  71. package/dist/lib/stdin.js +1 -2
  72. package/dist/lib/subprocess.d.ts +1 -1
  73. package/dist/lib/subprocess.js +1 -1
  74. package/dist/lib/ticket-body.d.ts +22 -6
  75. package/dist/lib/ticket-body.js +71 -9
  76. package/dist/lib/ticket-lookup.d.ts +53 -0
  77. package/dist/lib/ticket-lookup.js +78 -0
  78. package/dist/lib/ticket-report.d.ts +7 -0
  79. package/dist/lib/ticket-report.js +14 -0
  80. package/oclif.manifest.json +3931 -2876
  81. package/opencli.json +860 -113
  82. package/package.json +4 -1
@@ -1,4 +1,11 @@
1
+ import { type LengthCheck } from './limits';
1
2
  export declare const ticketBodyFlags: {
3
+ 'assignee-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
4
+ 'milestone-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
5
+ 'parent-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
6
+ 'platform-id': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ status: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ priority: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
2
9
  description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
3
10
  'technical-analysis': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
4
11
  scenarios: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -9,12 +16,10 @@ export declare const ticketBodyFlags: {
9
16
  condition: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
17
  weight: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
18
  'due-at': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
- 'status-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
13
- 'priority-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
14
- 'assignee-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
15
- 'milestone-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
16
- 'parent-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
17
- 'platform-id': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
19
+ };
20
+ export declare const CLEARABLE_FIELDS: Record<string, string>;
21
+ export declare const clearFlag: {
22
+ clear: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
18
23
  };
19
24
  type BodyFlags = Record<string, unknown>;
20
25
  export declare function scenariosFromFlags(flags: BodyFlags): Array<Record<string, unknown>> | undefined;
@@ -22,4 +27,15 @@ export declare function conditionsFromFlags(flags: BodyFlags): Array<{
22
27
  text: string;
23
28
  }> | undefined;
24
29
  export declare function applyTicketBody(body: BodyFlags, flags: BodyFlags): void;
30
+ /**
31
+ * I campi del corpo che il server misura, pronti per `tooLongMessage` (CYCL-32): titolo, descrizione
32
+ * e analisi tecnica. Scenari e condizioni non ci sono — non hanno tetto.
33
+ *
34
+ * `current` è il ticket com'è adesso, per i comandi che lo leggono prima di scrivere: fa da
35
+ * salvaguardia, così un testo scritto prima che il tetto esistesse resta correggibile finché non si
36
+ * allunga. Chi non lo conosce lo omette e il tetto vale pieno.
37
+ */
38
+ export declare function ticketLengthChecks(flags: BodyFlags, current?: BodyFlags): LengthCheck[];
39
+ /** Marca come vuoti i campi elencati da `--clear`: null, o lista vuota per le piattaforme. */
40
+ export declare function applyTicketClears(body: BodyFlags, fields: string[] | undefined): void;
25
41
  export {};
@@ -1,17 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ticketBodyFlags = void 0;
3
+ exports.clearFlag = exports.CLEARABLE_FIELDS = exports.ticketBodyFlags = void 0;
4
4
  exports.scenariosFromFlags = scenariosFromFlags;
5
5
  exports.conditionsFromFlags = conditionsFromFlags;
6
6
  exports.applyTicketBody = applyTicketBody;
7
+ exports.ticketLengthChecks = ticketLengthChecks;
8
+ exports.applyTicketClears = applyTicketClears;
7
9
  const core_1 = require("@oclif/core");
10
+ const limits_1 = require("./limits");
11
+ const ticket_lookup_1 = require("./ticket-lookup");
8
12
  // Flag del corpo ticket condivisi da `tickets create` e `tickets update`: descrizione (sommario
9
13
  // human-simple) + N scenari BDD + N condizioni DoD + analisi tecnica (registro tecnico separato) +
10
14
  // metadati. Gli scenari si passano come JSON (--scenarios) oppure, per un SINGOLO scenario, con le
11
15
  // scorciatoie --step-*. Le condizioni DoD sono --condition (ripetibile). Parità piena col form web.
12
16
  exports.ticketBodyFlags = {
13
- description: core_1.Flags.string({ description: 'Free-form description (human-simple summary)' }),
14
- 'technical-analysis': core_1.Flags.string({ description: 'Technical analysis (stack, cause) — kept out of the human-simple body' }),
17
+ description: core_1.Flags.string({
18
+ description: `Free-form description (human-simple summary, max ${limits_1.LENGTH_LIMITS.description} characters)`,
19
+ }),
20
+ 'technical-analysis': core_1.Flags.string({
21
+ description: `Technical analysis (stack, cause) — kept out of the human-simple body (max ${limits_1.LENGTH_LIMITS.technicalAnalysis} characters)`,
22
+ }),
15
23
  scenarios: core_1.Flags.string({
16
24
  description: 'BDD scenarios as a JSON array: [{"title","step_given","step_when","step_then","step_expected"}]',
17
25
  }),
@@ -22,13 +30,34 @@ exports.ticketBodyFlags = {
22
30
  condition: core_1.Flags.string({ description: 'Definition-of-Done line (repeatable)', multiple: true }),
23
31
  weight: core_1.Flags.string({ description: 'Story points (positive integer)' }),
24
32
  'due-at': core_1.Flags.string({ description: 'Due date (ISO 8601)' }),
25
- 'status-id': core_1.Flags.string({ description: 'Status lookup id' }),
26
- 'priority-id': core_1.Flags.string({ description: 'Priority lookup id' }),
33
+ // Stato e priorità si indicano per code, etichetta o id, e il comando li risolve (CYCL-34).
34
+ ...ticket_lookup_1.ticketLookupFlags,
27
35
  'assignee-id': core_1.Flags.string({ description: 'Assignee account id' }),
28
36
  'milestone-id': core_1.Flags.string({ description: 'Milestone id (of this project)' }),
29
37
  'parent-id': core_1.Flags.string({ description: 'Parent epic id (of this project) — pass an empty string to detach' }),
30
38
  'platform-id': core_1.Flags.string({ description: 'Affected platform id (repeatable)', multiple: true }),
31
39
  };
40
+ // Campi che `tickets update` svuota solo se glielo si chiede (`--clear <campo>`): nome del flag che
41
+ // li imposta → chiave del corpo. Svuotarli è una richiesta a sé, mai l'effetto collaterale di un
42
+ // campo omesso (CYCL-30). Scenari e condizioni non ci sono: il backend li sostituisce quando il
43
+ // corpo ne porta di nuovi, li lascia intatti quando li omette, e un array vuoto non li cancella.
44
+ exports.CLEARABLE_FIELDS = {
45
+ description: 'description',
46
+ 'technical-analysis': 'technical_analysis',
47
+ weight: 'weight',
48
+ 'due-at': 'due_at',
49
+ assignee: 'assignee_id',
50
+ milestone: 'milestone_id',
51
+ parent: 'parent_id',
52
+ platforms: 'platform_ids',
53
+ };
54
+ exports.clearFlag = {
55
+ clear: core_1.Flags.string({
56
+ description: `Empty a field on purpose (repeatable): ${Object.keys(exports.CLEARABLE_FIELDS).join(', ')}`,
57
+ multiple: true,
58
+ options: Object.keys(exports.CLEARABLE_FIELDS),
59
+ }),
60
+ };
32
61
  const STEP_KEYS = [
33
62
  ['step-given', 'step_given'],
34
63
  ['step-when', 'step_when'],
@@ -64,6 +93,8 @@ function conditionsFromFlags(flags) {
64
93
  return conditions.map((text) => ({ text }));
65
94
  }
66
95
  // Popola `body` coi campi del corpo presenti nei flag (chiavi snake_case attese dall'API CLI).
96
+ // Stato e priorità restano fuori: vanno risolti in id contro le lookup dell'organizzazione, cioè
97
+ // con una chiamata, e ogni comando li aggiunge dopo — con o senza predefinito (CYCL-34).
67
98
  function applyTicketBody(body, flags) {
68
99
  if (flags.description !== undefined)
69
100
  body.description = flags.description;
@@ -73,10 +104,6 @@ function applyTicketBody(body, flags) {
73
104
  body.weight = flags.weight;
74
105
  if (flags['due-at'] !== undefined)
75
106
  body.due_at = flags['due-at'];
76
- if (flags['status-id'] !== undefined)
77
- body.status_id = flags['status-id'];
78
- if (flags['priority-id'] !== undefined)
79
- body.priority_id = flags['priority-id'];
80
107
  if (flags['assignee-id'] !== undefined)
81
108
  body.assignee_id = flags['assignee-id'];
82
109
  if (flags['milestone-id'] !== undefined)
@@ -92,3 +119,38 @@ function applyTicketBody(body, flags) {
92
119
  if (conditions !== undefined)
93
120
  body.conditions_attributes = conditions;
94
121
  }
122
+ /**
123
+ * I campi del corpo che il server misura, pronti per `tooLongMessage` (CYCL-32): titolo, descrizione
124
+ * e analisi tecnica. Scenari e condizioni non ci sono — non hanno tetto.
125
+ *
126
+ * `current` è il ticket com'è adesso, per i comandi che lo leggono prima di scrivere: fa da
127
+ * salvaguardia, così un testo scritto prima che il tetto esistesse resta correggibile finché non si
128
+ * allunga. Chi non lo conosce lo omette e il tetto vale pieno.
129
+ */
130
+ function ticketLengthChecks(flags, current = {}) {
131
+ // `orthography`: il server corregge l'ortografia italiana di questi tre campi prima di misurarli.
132
+ return [
133
+ { flag: '--title', value: flags.title, max: limits_1.LENGTH_LIMITS.title, previous: current.title, orthography: true },
134
+ {
135
+ flag: '--description',
136
+ value: flags.description,
137
+ max: limits_1.LENGTH_LIMITS.description,
138
+ previous: current.description,
139
+ orthography: true,
140
+ },
141
+ {
142
+ flag: '--technical-analysis',
143
+ value: flags['technical-analysis'],
144
+ max: limits_1.LENGTH_LIMITS.technicalAnalysis,
145
+ previous: current.technical_analysis,
146
+ orthography: true,
147
+ },
148
+ ];
149
+ }
150
+ /** Marca come vuoti i campi elencati da `--clear`: null, o lista vuota per le piattaforme. */
151
+ function applyTicketClears(body, fields) {
152
+ for (const field of fields ?? []) {
153
+ const key = exports.CLEARABLE_FIELDS[field];
154
+ body[key] = key === 'platform_ids' ? [] : null;
155
+ }
156
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Le due lookup d'organizzazione che ogni ticket si porta addosso: stato e priorità (CYCL-34).
3
+ *
4
+ * Il server le pretende entrambe alla creazione (R422-TICKET-001 "Status must exist and Priority
5
+ * must exist") mentre l'aiuto della CLI le dava per opzionali, e i loro id andavano cercati prima
6
+ * con `tickets statuses`/`tickets priorities` e incollati come UUID. Qui vivono l'endpoint da
7
+ * leggere, il code che vale quando il ticket nasce senza che li si sia indicati, e come si chiama
8
+ * il campo nei messaggi.
9
+ */
10
+ export interface TicketLookup {
11
+ /** Lookup d'organizzazione (non di progetto): gli id valgono per un ticket di qualunque progetto. */
12
+ path: string;
13
+ /** Nome del campo nei messaggi d'errore. */
14
+ name: string;
15
+ /** Il flag che lo imposta. */
16
+ flag: string;
17
+ /** Il code che vale quando il ticket nasce e non lo si è indicato. */
18
+ fallback: string;
19
+ errorCode: string;
20
+ }
21
+ export declare const TICKET_STATUS: TicketLookup;
22
+ export declare const TICKET_PRIORITY: TicketLookup;
23
+ /**
24
+ * `--status`/`--priority` di un comando che modifica un ticket già esistente. `--status-id` e
25
+ * `--priority-id` restano come alias: erano l'unico modo di indicarli, e gli script che li usano
26
+ * continuano a funzionare senza riscritture.
27
+ */
28
+ export declare const ticketLookupFlags: {
29
+ status: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
30
+ priority: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
31
+ };
32
+ /** Gli stessi due flag su un comando che crea il ticket, dove ometterli ha un valore predefinito. */
33
+ export declare const newTicketLookupFlags: {
34
+ status: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
35
+ priority: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
36
+ };
37
+ /**
38
+ * Le voci che corrispondono al riferimento scritto a mano: prima il code, poi l'etichetta, senza
39
+ * badare alle maiuscole. Il code ha la precedenza perché è il nome esatto della voce: senza,
40
+ * l'etichetta di un'altra ("Urgent" su `high`) farebbe concorrenza al code di quella cercata
41
+ * (`urgent`) e una scelta legittima diventerebbe un'ambiguità da rifiutare.
42
+ */
43
+ export declare function matchTicketLookup(rows: unknown, value: string): Array<{
44
+ id: string;
45
+ }>;
46
+ /**
47
+ * Il rifiuto di un riferimento che non si risolve: dice cos'è stato cercato, come si scrive il flag
48
+ * e quali voci esistono davvero — così scoprirle non costa un secondo comando.
49
+ */
50
+ export declare function ticketLookupErrorMessage(lookup: TicketLookup, wanted: string, rows: unknown, { matched, isDefault }: {
51
+ matched: number;
52
+ isDefault: boolean;
53
+ }): string;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.newTicketLookupFlags = exports.ticketLookupFlags = exports.TICKET_PRIORITY = exports.TICKET_STATUS = void 0;
4
+ exports.matchTicketLookup = matchTicketLookup;
5
+ exports.ticketLookupErrorMessage = ticketLookupErrorMessage;
6
+ const core_1 = require("@oclif/core");
7
+ const error_codes_1 = require("../errors/error-codes");
8
+ exports.TICKET_STATUS = {
9
+ path: '/cli/v1/types/ticket_statuses',
10
+ name: 'Status',
11
+ flag: '--status',
12
+ fallback: 'open',
13
+ errorCode: error_codes_1.ErrorCodes.TicketStatus.notFound,
14
+ };
15
+ exports.TICKET_PRIORITY = {
16
+ path: '/cli/v1/types/ticket_priorities',
17
+ name: 'Priority',
18
+ flag: '--priority',
19
+ fallback: 'medium',
20
+ errorCode: error_codes_1.ErrorCodes.TicketPriority.notFound,
21
+ };
22
+ /**
23
+ * `--status`/`--priority` di un comando che modifica un ticket già esistente. `--status-id` e
24
+ * `--priority-id` restano come alias: erano l'unico modo di indicarli, e gli script che li usano
25
+ * continuano a funzionare senza riscritture.
26
+ */
27
+ exports.ticketLookupFlags = {
28
+ status: core_1.Flags.string({
29
+ aliases: ['status-id'],
30
+ description: 'Status code, label or id (e.g. open, "In Progress") — unchanged when omitted',
31
+ }),
32
+ priority: core_1.Flags.string({
33
+ aliases: ['priority-id'],
34
+ description: 'Priority code, label or id (e.g. medium, High) — unchanged when omitted',
35
+ }),
36
+ };
37
+ /** Gli stessi due flag su un comando che crea il ticket, dove ometterli ha un valore predefinito. */
38
+ exports.newTicketLookupFlags = {
39
+ status: core_1.Flags.string({
40
+ aliases: ['status-id'],
41
+ description: `Status code, label or id (e.g. open, "In Progress") — default: ${exports.TICKET_STATUS.fallback}`,
42
+ }),
43
+ priority: core_1.Flags.string({
44
+ aliases: ['priority-id'],
45
+ description: `Priority code, label or id (e.g. medium, High) — default: ${exports.TICKET_PRIORITY.fallback}`,
46
+ }),
47
+ };
48
+ /**
49
+ * Le voci che corrispondono al riferimento scritto a mano: prima il code, poi l'etichetta, senza
50
+ * badare alle maiuscole. Il code ha la precedenza perché è il nome esatto della voce: senza,
51
+ * l'etichetta di un'altra ("Urgent" su `high`) farebbe concorrenza al code di quella cercata
52
+ * (`urgent`) e una scelta legittima diventerebbe un'ambiguità da rifiutare.
53
+ */
54
+ function matchTicketLookup(rows, value) {
55
+ const wanted = value.trim().toLowerCase();
56
+ const list = (Array.isArray(rows) ? rows : []);
57
+ const identified = list.filter((row) => typeof row.id === 'string' && row.id !== '');
58
+ const sameAs = (field) => typeof field === 'string' && field.trim().toLowerCase() === wanted;
59
+ const byCode = identified.filter((row) => sameAs(row.code));
60
+ return byCode.length > 0 ? byCode : identified.filter((row) => sameAs(row.label));
61
+ }
62
+ /**
63
+ * Il rifiuto di un riferimento che non si risolve: dice cos'è stato cercato, come si scrive il flag
64
+ * e quali voci esistono davvero — così scoprirle non costa un secondo comando.
65
+ */
66
+ function ticketLookupErrorMessage(lookup, wanted, rows, { matched, isDefault }) {
67
+ const known = (Array.isArray(rows) ? rows : [])
68
+ .map((row) => (typeof row.code === 'string' && row.code !== '' ? row.code : String(row.label ?? '')))
69
+ .filter((name) => name !== '')
70
+ .join(', ');
71
+ const hint = `Pass ${lookup.flag} with a code, a label or an id${known === '' ? '' : ` — known: ${known}`}.`;
72
+ if (matched > 1)
73
+ return `${lookup.name} "${wanted}" matches ${matched} entries. ${hint}`;
74
+ if (isDefault) {
75
+ return `This organization has no ${lookup.name.toLowerCase()} "${wanted}", the default for a new ticket. ${hint}`;
76
+ }
77
+ return `${lookup.name} not found: "${wanted}". ${hint}`;
78
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Una versione del resoconto di lavorazione: intestazione coi metadati + il testo per intero
3
+ * (CYCL-31). Il corpo passa da `sanitizeMultiline` e non da `renderRecord`: quest'ultimo appiattisce
4
+ * ogni valore con `sanitize`, che trasforma gli a capo in spazi — su un resoconto di migliaia di
5
+ * caratteri significherebbe stamparlo come un muro di testo su una riga sola.
6
+ */
7
+ export declare function renderReport(report: Record<string, unknown>): string;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderReport = renderReport;
4
+ const output_1 = require("./output");
5
+ /**
6
+ * Una versione del resoconto di lavorazione: intestazione coi metadati + il testo per intero
7
+ * (CYCL-31). Il corpo passa da `sanitizeMultiline` e non da `renderRecord`: quest'ultimo appiattisce
8
+ * ogni valore con `sanitize`, che trasforma gli a capo in spazi — su un resoconto di migliaia di
9
+ * caratteri significherebbe stamparlo come un muro di testo su una riga sola.
10
+ */
11
+ function renderReport(report) {
12
+ const header = `── version ${(0, output_1.sanitize)(report.version ?? '?')} · ${(0, output_1.sanitize)(report.author ?? '?')} · ${(0, output_1.sanitize)(report.source ?? '-')} · ${(0, output_1.sanitize)(report.created_at ?? '-')}`;
13
+ return `${header}\n${(0, output_1.sanitizeMultiline)(report.body ?? '')}`;
14
+ }