@enrichlayer/el-linear 1.44.0 → 1.44.2

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.
@@ -99,6 +99,38 @@ el-linear issues read DEV-123 --format json 2>&1 | python3 -c "import json,sys;
99
99
 
100
100
  `--body` is mutually exclusive with `--field` / `--sections` / `--with` (those extract named parts or extend the JSON envelope; `--body` is the whole thing as text).
101
101
 
102
+ #### Citing an issue's stated rationale: `--body` or `--field`, never `--format summary`
103
+
104
+ **`--format summary` truncates the description.** That is correct for scanning a board and wrong the moment you quote, cite, or reason from what an issue *says*. A research pass once read an issue with `--format summary`, saw a truncated description, and published the opposite of the design position that issue states outright — the fix was one command with a different flag, run twenty minutes too late.
105
+
106
+ So: the moment a claim is about an issue's **stated rationale** — what a decision claimed at the time, why an approach was chosen, what a spec requires — read `--body` (or `--field <section>` for one section) and quote from that.
107
+
108
+ ```bash
109
+ # ✅ Reasoning from what the issue actually says
110
+ el-linear issues read DEV-123 --body 2>&1
111
+ el-linear issues read DEV-123 --field "Why we need this" 2>&1
112
+
113
+ # ❌ Citing a summary — the description you are quoting may be cut off
114
+ el-linear issues read DEV-123 --format summary 2>&1
115
+ ```
116
+
117
+ The scope is narrow and worth stating precisely: an issue body is a weak source for *system behavior* — it records what someone intended, not what the code does — but it is the **authoritative** artifact for what a decision claimed at the time. Use it for the second, not the first.
118
+
119
+ #### An umbrella's status is not delivery evidence
120
+
121
+ A parent or umbrella issue's own status says nothing reliable about whether the work shipped. Read its children and slices, then read the artifact.
122
+
123
+ ```bash
124
+ el-linear issues related DEV-100 --format summary 2>&1
125
+ ```
126
+
127
+ Neither direction is safe on its own:
128
+
129
+ - **Canceled does not mean abandoned.** An umbrella is routinely closed as bookkeeping after its slices land — one was reported as "abandoned" in a research document while all six of its children were Done and the code was in production.
130
+ - **Done children do not prove delivery either.** A child can be a duplicate, a rename, or an administrative closure.
131
+
132
+ The tracker is a lead. The **merged MR or the deployed code** is the evidence — go look at it before writing "shipped" or "abandoned".
133
+
102
134
  ### Comment reads and full comment bodies
103
135
 
104
136
  When you need a specific comment, or the full text of a long comment, use the
@@ -185,6 +217,14 @@ Every issue should communicate **why** the work matters and **what** success loo
185
217
  ### Example
186
218
 
187
219
  ```markdown
220
+ ## Intake decision
221
+ - Needed: Yes — contact tracking is split across spreadsheets, Linear, and memory.
222
+ - Worth doing: Yes — outbound scale makes the fragmentation costly now, not later.
223
+ - Existing work: searched "CRM", "contact tracking" with --include-closed; no duplicate.
224
+ - Owner: Nico (operations tooling).
225
+ - Placement: OPS / Sales infrastructure; self-hosted on the Hetzner box.
226
+ - Decision: PROCEED
227
+
188
228
  ## Set up a self-hosted CRM
189
229
 
190
230
  The team needs a CRM to replace fragmented contact tracking
@@ -203,6 +243,26 @@ and outreach tracked in one place.
203
243
 
204
244
  ---
205
245
 
246
+ ## Intake Decision Block (MANDATORY, blocking)
247
+
248
+ **`el-linear issues create` refuses any description without a `## Intake decision` section.** It must open the description, and the six lines must appear in this order:
249
+
250
+ ```markdown
251
+ ## Intake decision
252
+ - Needed: Yes — <why this is needed>
253
+ - Worth doing: Yes — <why the value exceeds the cost>
254
+ - Existing work: <duplicate/search result and evidence>
255
+ - Owner: <canonical owner or source of truth>
256
+ - Placement: <team/project/repository/document path>
257
+ - Decision: PROCEED
258
+ ```
259
+
260
+ Write it **before** composing the rest of the body. The gate runs before the create POST, so omitting it fails the call outright — nothing is written, and a finished issue body then has to be reassembled around a section you were never told to include.
261
+
262
+ The point is that the decision is *recorded*, not merely reached: `Existing work` cites the search you actually ran, and `Owner`/`Placement` name a concrete destination rather than a plausible one. It is the same discipline the two gates below enforce mechanically, applied to the judgment they cannot check.
263
+
264
+ Escape hatch: `--allow-missing-intake-decision`, which is recorded. It exists for an **accountable human** who has approved an exceptional create — not for an agent that would rather not write the block. `--skip-validation` also bypasses it but disables every other field check too, so prefer the narrow flag.
265
+
206
266
  ## Duplicate & Related Issues Check (MANDATORY)
207
267
 
208
268
  **Search before creating. No exceptions.**
@@ -374,6 +434,7 @@ Don't start implementation work on an unassigned issue — the assignee is the p
374
434
 
375
435
  Complete ALL items before creating any issue:
376
436
 
437
+ - [ ] **Intake decision** — description opens with the `## Intake decision` block (above). Blocking.
377
438
  - [ ] **Duplicate & related check** — searched for existing issues, linked related ones (above).
378
439
  - [ ] **Team** — ask user if unclear (`el-linear teams list`).
379
440
  - [ ] **Assignee** — ask user if unclear (`el-linear users list --active`).
@@ -63,7 +63,11 @@ const WRAPPED_IN_EMPHASIS = /^([*_]{1,3})([^*_].*?)\1$/;
63
63
  const INLINE_EMPHASIS = /(^|[^\p{L}\p{N}])([*_]{1,3})(?=\S)(.+?\S)\2(?=$|[^\p{L}\p{N}])/gu;
64
64
  const PLACEHOLDER = /^(?:tbd|todo|unknown|n\/?a|none|unsure|not decided|-)\.?$/i;
65
65
  const NON_SPECIFIC = /^(?:yes|no)$/i;
66
- const AFFIRMATIVE_WITH_REASON = /^yes\s*(?:[-—:;,]|because)\s*(\S.{2,})$/i;
66
+ // The gate exists to force an explicit verdict AND a reason. Which punctuation
67
+ // joins the two carries no meaning, so every ordinary separator is accepted —
68
+ // including `.`, which reads most naturally when the reason is a full sentence.
69
+ // A bare verdict still fails: the trailing group requires real reason text.
70
+ const AFFIRMATIVE_WITH_REASON = /^yes\s*(?:[-–—.:;,]|because)\s*(\S.{2,})$/i;
67
71
  function labelOf(key) {
68
72
  return FIELD_DEFINITIONS.find((field) => field.key === key)?.label ?? key;
69
73
  }
@@ -252,7 +256,7 @@ function describeFieldProblem(field, problem, value) {
252
256
  case "non-specific":
253
257
  return `The intake field "${field}" reads "${quote(value)}", which is too short or too generic to be a specific answer.`;
254
258
  case "no-judgment":
255
- return `The intake field "${field}" reads "${quote(value)}" — the content is there, but it is not an explicit "Yes — <reason>" judgment.`;
259
+ return `The intake field "${field}" reads "${quote(value)}" — the verdict is there, but no reason follows it. Write both together, e.g. "Yes — <reason>" or "Yes. <reason>"; any ordinary separator works.`;
256
260
  case "placeholder-reason":
257
261
  return `The intake field "${field}" says Yes but gives the placeholder reason "${quote(value)}"; record the real reason.`;
258
262
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enrichlayer/el-linear",
3
- "version": "1.44.0",
3
+ "version": "1.44.2",
4
4
  "description": "A pragmatic CLI for Linear.app — deterministic team/label/member resolution, structured issue validation, configurable term enforcement, and a GraphQL escape hatch.",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@inquirer/prompts": "^8.4.2",
55
- "@linear/sdk": "^87.0.0",
55
+ "@linear/sdk": "^88.0.0",
56
56
  "commander": "^15.0.0",
57
57
  "picocolors": "^1.1.1"
58
58
  },