openplanr 1.2.7 → 1.3.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 (164) hide show
  1. package/README.md +41 -4
  2. package/dist/agents/task-parser.d.ts.map +1 -1
  3. package/dist/agents/task-parser.js +8 -34
  4. package/dist/agents/task-parser.js.map +1 -1
  5. package/dist/ai/prompts/prompt-builder.d.ts +48 -0
  6. package/dist/ai/prompts/prompt-builder.d.ts.map +1 -1
  7. package/dist/ai/prompts/prompt-builder.js +57 -1
  8. package/dist/ai/prompts/prompt-builder.js.map +1 -1
  9. package/dist/ai/prompts/system-prompts.d.ts +24 -1
  10. package/dist/ai/prompts/system-prompts.d.ts.map +1 -1
  11. package/dist/ai/prompts/system-prompts.js +104 -6
  12. package/dist/ai/prompts/system-prompts.js.map +1 -1
  13. package/dist/ai/schemas/ai-response-schemas.d.ts +68 -0
  14. package/dist/ai/schemas/ai-response-schemas.d.ts.map +1 -1
  15. package/dist/ai/schemas/ai-response-schemas.js +81 -0
  16. package/dist/ai/schemas/ai-response-schemas.js.map +1 -1
  17. package/dist/ai/types.d.ts +2 -0
  18. package/dist/ai/types.d.ts.map +1 -1
  19. package/dist/ai/types.js +4 -0
  20. package/dist/ai/types.js.map +1 -1
  21. package/dist/cli/commands/backlog.d.ts +12 -0
  22. package/dist/cli/commands/backlog.d.ts.map +1 -1
  23. package/dist/cli/commands/backlog.js +88 -2
  24. package/dist/cli/commands/backlog.js.map +1 -1
  25. package/dist/cli/commands/config.d.ts.map +1 -1
  26. package/dist/cli/commands/config.js +8 -2
  27. package/dist/cli/commands/config.js.map +1 -1
  28. package/dist/cli/commands/linear.d.ts +8 -0
  29. package/dist/cli/commands/linear.d.ts.map +1 -0
  30. package/dist/cli/commands/linear.js +550 -0
  31. package/dist/cli/commands/linear.js.map +1 -0
  32. package/dist/cli/commands/quick.d.ts +17 -0
  33. package/dist/cli/commands/quick.d.ts.map +1 -1
  34. package/dist/cli/commands/quick.js +31 -15
  35. package/dist/cli/commands/quick.js.map +1 -1
  36. package/dist/cli/commands/revise.d.ts +24 -0
  37. package/dist/cli/commands/revise.d.ts.map +1 -0
  38. package/dist/cli/commands/revise.js +570 -0
  39. package/dist/cli/commands/revise.js.map +1 -0
  40. package/dist/cli/index.js +4 -0
  41. package/dist/cli/index.js.map +1 -1
  42. package/dist/models/schema.d.ts +43 -0
  43. package/dist/models/schema.d.ts.map +1 -1
  44. package/dist/models/schema.js +49 -0
  45. package/dist/models/schema.js.map +1 -1
  46. package/dist/models/types.d.ts +296 -0
  47. package/dist/models/types.d.ts.map +1 -1
  48. package/dist/services/artifact-gathering.d.ts +4 -0
  49. package/dist/services/artifact-gathering.d.ts.map +1 -1
  50. package/dist/services/artifact-gathering.js +1 -1
  51. package/dist/services/artifact-gathering.js.map +1 -1
  52. package/dist/services/artifact-service.d.ts +12 -1
  53. package/dist/services/artifact-service.d.ts.map +1 -1
  54. package/dist/services/artifact-service.js +49 -6
  55. package/dist/services/artifact-service.js.map +1 -1
  56. package/dist/services/atomic-write-service.d.ts +41 -0
  57. package/dist/services/atomic-write-service.d.ts.map +1 -0
  58. package/dist/services/atomic-write-service.js +87 -0
  59. package/dist/services/atomic-write-service.js.map +1 -0
  60. package/dist/services/audit-log-service.d.ts +47 -0
  61. package/dist/services/audit-log-service.d.ts.map +1 -0
  62. package/dist/services/audit-log-service.js +210 -0
  63. package/dist/services/audit-log-service.js.map +1 -0
  64. package/dist/services/cascade-service.d.ts +62 -0
  65. package/dist/services/cascade-service.d.ts.map +1 -0
  66. package/dist/services/cascade-service.js +189 -0
  67. package/dist/services/cascade-service.js.map +1 -0
  68. package/dist/services/credentials-service.js +2 -2
  69. package/dist/services/credentials-service.js.map +1 -1
  70. package/dist/services/diff-service.d.ts +18 -0
  71. package/dist/services/diff-service.d.ts.map +1 -0
  72. package/dist/services/diff-service.js +35 -0
  73. package/dist/services/diff-service.js.map +1 -0
  74. package/dist/services/evidence-verifier.d.ts +71 -0
  75. package/dist/services/evidence-verifier.d.ts.map +1 -0
  76. package/dist/services/evidence-verifier.js +174 -0
  77. package/dist/services/evidence-verifier.js.map +1 -0
  78. package/dist/services/git-service.d.ts +60 -0
  79. package/dist/services/git-service.d.ts.map +1 -0
  80. package/dist/services/git-service.js +137 -0
  81. package/dist/services/git-service.js.map +1 -0
  82. package/dist/services/graph-integrity.d.ts +35 -0
  83. package/dist/services/graph-integrity.d.ts.map +1 -0
  84. package/dist/services/graph-integrity.js +53 -0
  85. package/dist/services/graph-integrity.js.map +1 -0
  86. package/dist/services/linear/body-formatters.d.ts +69 -0
  87. package/dist/services/linear/body-formatters.d.ts.map +1 -0
  88. package/dist/services/linear/body-formatters.js +183 -0
  89. package/dist/services/linear/body-formatters.js.map +1 -0
  90. package/dist/services/linear/constants.d.ts +61 -0
  91. package/dist/services/linear/constants.d.ts.map +1 -0
  92. package/dist/services/linear/constants.js +84 -0
  93. package/dist/services/linear/constants.js.map +1 -0
  94. package/dist/services/linear/errors.d.ts +14 -0
  95. package/dist/services/linear/errors.d.ts.map +1 -0
  96. package/dist/services/linear/errors.js +106 -0
  97. package/dist/services/linear/errors.js.map +1 -0
  98. package/dist/services/linear/estimate-resolver.d.ts +50 -0
  99. package/dist/services/linear/estimate-resolver.d.ts.map +1 -0
  100. package/dist/services/linear/estimate-resolver.js +82 -0
  101. package/dist/services/linear/estimate-resolver.js.map +1 -0
  102. package/dist/services/linear/plan-builders.d.ts +64 -0
  103. package/dist/services/linear/plan-builders.d.ts.map +1 -0
  104. package/dist/services/linear/plan-builders.js +237 -0
  105. package/dist/services/linear/plan-builders.js.map +1 -0
  106. package/dist/services/linear/scope-loaders.d.ts +79 -0
  107. package/dist/services/linear/scope-loaders.d.ts.map +1 -0
  108. package/dist/services/linear/scope-loaders.js +227 -0
  109. package/dist/services/linear/scope-loaders.js.map +1 -0
  110. package/dist/services/linear/strategy-context.d.ts +66 -0
  111. package/dist/services/linear/strategy-context.d.ts.map +1 -0
  112. package/dist/services/linear/strategy-context.js +121 -0
  113. package/dist/services/linear/strategy-context.js.map +1 -0
  114. package/dist/services/linear-mapping-service.d.ts +11 -0
  115. package/dist/services/linear-mapping-service.d.ts.map +1 -0
  116. package/dist/services/linear-mapping-service.js +220 -0
  117. package/dist/services/linear-mapping-service.js.map +1 -0
  118. package/dist/services/linear-pull-service.d.ts +137 -0
  119. package/dist/services/linear-pull-service.d.ts.map +1 -0
  120. package/dist/services/linear-pull-service.js +720 -0
  121. package/dist/services/linear-pull-service.js.map +1 -0
  122. package/dist/services/linear-push-service.d.ts +86 -0
  123. package/dist/services/linear-push-service.d.ts.map +1 -0
  124. package/dist/services/linear-push-service.js +956 -0
  125. package/dist/services/linear-push-service.js.map +1 -0
  126. package/dist/services/linear-service.d.ts +122 -0
  127. package/dist/services/linear-service.d.ts.map +1 -0
  128. package/dist/services/linear-service.js +361 -0
  129. package/dist/services/linear-service.js.map +1 -0
  130. package/dist/services/prompt-service.d.ts +37 -0
  131. package/dist/services/prompt-service.d.ts.map +1 -1
  132. package/dist/services/prompt-service.js +111 -0
  133. package/dist/services/prompt-service.js.map +1 -1
  134. package/dist/services/revise-apply-service.d.ts +55 -0
  135. package/dist/services/revise-apply-service.d.ts.map +1 -0
  136. package/dist/services/revise-apply-service.js +255 -0
  137. package/dist/services/revise-apply-service.js.map +1 -0
  138. package/dist/services/revise-cache-service.d.ts +46 -0
  139. package/dist/services/revise-cache-service.d.ts.map +1 -0
  140. package/dist/services/revise-cache-service.js +88 -0
  141. package/dist/services/revise-cache-service.js.map +1 -0
  142. package/dist/services/revise-plan-service.d.ts +38 -0
  143. package/dist/services/revise-plan-service.d.ts.map +1 -0
  144. package/dist/services/revise-plan-service.js +151 -0
  145. package/dist/services/revise-plan-service.js.map +1 -0
  146. package/dist/services/revise-service.d.ts +115 -0
  147. package/dist/services/revise-service.d.ts.map +1 -0
  148. package/dist/services/revise-service.js +294 -0
  149. package/dist/services/revise-service.js.map +1 -0
  150. package/dist/services/template-sections.d.ts +28 -0
  151. package/dist/services/template-sections.d.ts.map +1 -0
  152. package/dist/services/template-sections.js +55 -0
  153. package/dist/services/template-sections.js.map +1 -0
  154. package/dist/templates/backlog/backlog-item.md.hbs +3 -0
  155. package/dist/templates/quick/quick-task.md.hbs +6 -0
  156. package/dist/utils/diff.d.ts +47 -0
  157. package/dist/utils/diff.d.ts.map +1 -0
  158. package/dist/utils/diff.js +278 -0
  159. package/dist/utils/diff.js.map +1 -0
  160. package/dist/utils/markdown.d.ts +23 -0
  161. package/dist/utils/markdown.d.ts.map +1 -1
  162. package/dist/utils/markdown.js +79 -0
  163. package/dist/utils/markdown.js.map +1 -1
  164. package/package.json +3 -2
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Git integration for `planr revise`.
3
+ *
4
+ * Two responsibilities:
5
+ *
6
+ * 1. **Clean-tree gate:** revise refuses to run with a dirty
7
+ * working tree by default. Users can override with `--allow-dirty`, but
8
+ * post-flight rollback depends on a clean pre-run state, so the gate is
9
+ * the load-bearing safety net.
10
+ *
11
+ * 2. **Capture + rollback anchor:** before bulk writes,
12
+ * revise captures HEAD and the set of touched paths so a post-flight
13
+ * graph-integrity failure can restore via `git checkout`.
14
+ *
15
+ * All git operations use `execFile` (not shell), matching the pattern in
16
+ * github-service.ts. If git is not available or the project is not a git
17
+ * repo, clean-tree checks fail closed (revise refuses to run) unless
18
+ * --allow-dirty is passed, because without git there is no safety net.
19
+ */
20
+ import { execFile } from 'node:child_process';
21
+ import { promisify } from 'node:util';
22
+ const execFileAsync = promisify(execFile);
23
+ const GIT_MAX_BUFFER = 10 * 1024 * 1024;
24
+ /**
25
+ * Inspect the working tree. Never throws — always returns a typed status so
26
+ * callers can render errors consistently.
27
+ */
28
+ export async function inspectGitTree(projectDir) {
29
+ let head;
30
+ try {
31
+ const { stdout } = await execFileAsync('git', ['rev-parse', 'HEAD'], {
32
+ cwd: projectDir,
33
+ maxBuffer: GIT_MAX_BUFFER,
34
+ });
35
+ head = stdout.trim();
36
+ }
37
+ catch (err) {
38
+ const message = err instanceof Error ? err.message : String(err);
39
+ if (/not a git repository|fatal:.*repository/i.test(message)) {
40
+ return { kind: 'not-a-repo', reason: message };
41
+ }
42
+ if (/ENOENT|git: not found|command not found/i.test(message)) {
43
+ return { kind: 'git-missing', reason: message };
44
+ }
45
+ // Empty repo (no commits yet) also reaches here; treat it as not-a-repo
46
+ // for clean-tree gating purposes — there is no HEAD to roll back to.
47
+ if (/unknown revision|does not have any commits/i.test(message)) {
48
+ return { kind: 'not-a-repo', reason: 'git repository has no commits yet' };
49
+ }
50
+ return { kind: 'not-a-repo', reason: message };
51
+ }
52
+ const { stdout: porcelain } = await execFileAsync('git', ['status', '--porcelain'], {
53
+ cwd: projectDir,
54
+ maxBuffer: GIT_MAX_BUFFER,
55
+ });
56
+ if (porcelain.trim().length === 0) {
57
+ return { kind: 'clean', head };
58
+ }
59
+ const changedPaths = porcelain
60
+ .split('\n')
61
+ .filter((line) => line.length > 0)
62
+ .map((line) => line.slice(3).trim());
63
+ return { kind: 'dirty', head, changedPaths };
64
+ }
65
+ /**
66
+ * The clean-tree gate: clean → pass; dirty → pass only when --allow-dirty;
67
+ * missing git / not a repo → fail closed unless --allow-dirty was passed
68
+ * (because without git there is no post-flight rollback safety net).
69
+ */
70
+ export async function checkCleanTree(projectDir, options) {
71
+ const status = await inspectGitTree(projectDir);
72
+ switch (status.kind) {
73
+ case 'clean':
74
+ return {
75
+ ok: true,
76
+ status,
77
+ message: `Working tree is clean at ${status.head.slice(0, 12)}.`,
78
+ };
79
+ case 'dirty':
80
+ if (options.allowDirty) {
81
+ return {
82
+ ok: true,
83
+ status,
84
+ message: `Working tree has ${status.changedPaths.length} uncommitted change(s); running with --allow-dirty. Post-flight rollback cannot restore these changes.`,
85
+ };
86
+ }
87
+ return {
88
+ ok: false,
89
+ status,
90
+ message: `Working tree has ${status.changedPaths.length} uncommitted change(s). Commit or stash them, or re-run with --allow-dirty (post-flight rollback cannot restore uncommitted work).`,
91
+ };
92
+ case 'not-a-repo':
93
+ if (options.allowDirty) {
94
+ return {
95
+ ok: true,
96
+ status,
97
+ message: `Not a git repository (${status.reason.trim()}); running with --allow-dirty. Post-flight rollback is disabled.`,
98
+ };
99
+ }
100
+ return {
101
+ ok: false,
102
+ status,
103
+ message: `Not a git repository (${status.reason.trim()}). Revise requires git for its post-flight rollback safety net. Initialize git, or re-run with --allow-dirty to opt out of the safety net.`,
104
+ };
105
+ case 'git-missing':
106
+ if (options.allowDirty) {
107
+ return {
108
+ ok: true,
109
+ status,
110
+ message: `git CLI not found; running with --allow-dirty. Post-flight rollback is disabled.`,
111
+ };
112
+ }
113
+ return {
114
+ ok: false,
115
+ status,
116
+ message: `git CLI not found on PATH. Revise requires git for its post-flight rollback safety net. Install git, or re-run with --allow-dirty to opt out.`,
117
+ };
118
+ default: {
119
+ const _exhaustive = status;
120
+ throw new Error(`unhandled GitTreeStatus: ${JSON.stringify(_exhaustive)}`);
121
+ }
122
+ }
123
+ }
124
+ /**
125
+ * Restore a set of paths from HEAD — the primitive the post-flight
126
+ * rollback invokes when graph integrity breaks after writes. Paths are
127
+ * relative to `projectDir`. Empty list is a no-op.
128
+ */
129
+ export async function checkoutPaths(projectDir, relativePaths) {
130
+ if (relativePaths.length === 0)
131
+ return;
132
+ await execFileAsync('git', ['checkout', '--', ...relativePaths], {
133
+ cwd: projectDir,
134
+ maxBuffer: GIT_MAX_BUFFER,
135
+ });
136
+ }
137
+ //# sourceMappingURL=git-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-service.js","sourceRoot":"","sources":["../../src/services/git-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC1C,MAAM,cAAc,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAmBxC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAAkB;IACrD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE;YACnE,GAAG,EAAE,UAAU;YACf,SAAS,EAAE,cAAc;SAC1B,CAAC,CAAC;QACH,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,IAAI,0CAA0C,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7D,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QACjD,CAAC;QACD,IAAI,0CAA0C,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7D,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAClD,CAAC;QACD,wEAAwE;QACxE,qEAAqE;QACrE,IAAI,6CAA6C,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAChE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,mCAAmC,EAAE,CAAC;QAC7E,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACjD,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE;QAClF,GAAG,EAAE,UAAU;QACf,SAAS,EAAE,cAAc;KAC1B,CAAC,CAAC;IAEH,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,MAAM,YAAY,GAAG,SAAS;SAC3B,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;SACjC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAEvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,UAAkB,EAClB,OAAiC;IAEjC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;IAEhD,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,OAAO;YACV,OAAO;gBACL,EAAE,EAAE,IAAI;gBACR,MAAM;gBACN,OAAO,EAAE,4BAA4B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG;aACjE,CAAC;QAEJ,KAAK,OAAO;YACV,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;gBACvB,OAAO;oBACL,EAAE,EAAE,IAAI;oBACR,MAAM;oBACN,OAAO,EAAE,oBAAoB,MAAM,CAAC,YAAY,CAAC,MAAM,wGAAwG;iBAChK,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM;gBACN,OAAO,EAAE,oBAAoB,MAAM,CAAC,YAAY,CAAC,MAAM,oIAAoI;aAC5L,CAAC;QAEJ,KAAK,YAAY;YACf,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;gBACvB,OAAO;oBACL,EAAE,EAAE,IAAI;oBACR,MAAM;oBACN,OAAO,EAAE,yBAAyB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kEAAkE;iBACzH,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM;gBACN,OAAO,EAAE,yBAAyB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,4IAA4I;aACnM,CAAC;QAEJ,KAAK,aAAa;YAChB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;gBACvB,OAAO;oBACL,EAAE,EAAE,IAAI;oBACR,MAAM;oBACN,OAAO,EAAE,kFAAkF;iBAC5F,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM;gBACN,OAAO,EAAE,+IAA+I;aACzJ,CAAC;QAEJ,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,WAAW,GAAU,MAAM,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB,EAAE,aAAuB;IAC7E,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACvC,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,EAAE;QAC/D,GAAG,EAAE,UAAU;QACf,SAAS,EAAE,cAAc;KAC1B,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Read-only artifact-graph integrity check for `planr revise` post-flight.
3
+ *
4
+ * Detects broken parent/child links after a revise run so the caller can
5
+ * trigger automatic rollback if the writes left the tree inconsistent. This
6
+ * is deliberately narrower than `planr sync`: it does not fix anything, does
7
+ * not write to the logger, and only looks at the relationships revise might
8
+ * have perturbed (parent-id frontmatter fields on features, stories, tasks).
9
+ *
10
+ * Why a separate module: keeps revise decoupled from the `sync` command's
11
+ * repair logic, which has different invariants (fixes links, prompts the
12
+ * user, etc.). The check here is a pure data function.
13
+ */
14
+ import type { ArtifactType, OpenPlanrConfig } from '../models/types.js';
15
+ export interface GraphIntegrityIssue {
16
+ childId: string;
17
+ childType: ArtifactType;
18
+ childPath: string;
19
+ parentField: 'epicId' | 'featureId' | 'storyId';
20
+ parentId: string;
21
+ reason: 'missing-parent' | 'parent-type-mismatch';
22
+ }
23
+ export interface GraphIntegrityReport {
24
+ ok: boolean;
25
+ issues: GraphIntegrityIssue[];
26
+ }
27
+ /**
28
+ * Walk every feature/story/task and verify its parent-id frontmatter
29
+ * resolves to an existing parent artifact of the correct type. Missing
30
+ * parent-id fields are tolerated (some tasks legitimately attach at
31
+ * feature level, for example) — only *broken* non-empty references are
32
+ * reported.
33
+ */
34
+ export declare function checkGraphIntegrity(projectDir: string, config: OpenPlanrConfig): Promise<GraphIntegrityReport>;
35
+ //# sourceMappingURL=graph-integrity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-integrity.d.ts","sourceRoot":"","sources":["../../src/services/graph-integrity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGxE,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,YAAY,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,gBAAgB,GAAG,sBAAsB,CAAC;CACnD;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,mBAAmB,EAAE,CAAC;CAC/B;AAcD;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,oBAAoB,CAAC,CA0B/B"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Read-only artifact-graph integrity check for `planr revise` post-flight.
3
+ *
4
+ * Detects broken parent/child links after a revise run so the caller can
5
+ * trigger automatic rollback if the writes left the tree inconsistent. This
6
+ * is deliberately narrower than `planr sync`: it does not fix anything, does
7
+ * not write to the logger, and only looks at the relationships revise might
8
+ * have perturbed (parent-id frontmatter fields on features, stories, tasks).
9
+ *
10
+ * Why a separate module: keeps revise decoupled from the `sync` command's
11
+ * repair logic, which has different invariants (fixes links, prompts the
12
+ * user, etc.). The check here is a pure data function.
13
+ */
14
+ import { listArtifacts, readArtifact } from './artifact-service.js';
15
+ const CHECKS = [
16
+ { childType: 'feature', parentField: 'epicId', parentType: 'epic' },
17
+ { childType: 'story', parentField: 'featureId', parentType: 'feature' },
18
+ { childType: 'task', parentField: 'storyId', parentType: 'story' },
19
+ ];
20
+ /**
21
+ * Walk every feature/story/task and verify its parent-id frontmatter
22
+ * resolves to an existing parent artifact of the correct type. Missing
23
+ * parent-id fields are tolerated (some tasks legitimately attach at
24
+ * feature level, for example) — only *broken* non-empty references are
25
+ * reported.
26
+ */
27
+ export async function checkGraphIntegrity(projectDir, config) {
28
+ const issues = [];
29
+ for (const spec of CHECKS) {
30
+ const children = await listArtifacts(projectDir, config, spec.childType);
31
+ for (const childRow of children) {
32
+ const child = await readArtifact(projectDir, config, spec.childType, childRow.id);
33
+ if (!child)
34
+ continue;
35
+ const parentId = child.data[spec.parentField];
36
+ if (!parentId)
37
+ continue; // optional ref — not a broken link
38
+ const parent = await readArtifact(projectDir, config, spec.parentType, parentId);
39
+ if (!parent) {
40
+ issues.push({
41
+ childId: childRow.id,
42
+ childType: spec.childType,
43
+ childPath: child.filePath,
44
+ parentField: spec.parentField,
45
+ parentId,
46
+ reason: 'missing-parent',
47
+ });
48
+ }
49
+ }
50
+ }
51
+ return { ok: issues.length === 0, issues };
52
+ }
53
+ //# sourceMappingURL=graph-integrity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-integrity.js","sourceRoot":"","sources":["../../src/services/graph-integrity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAsBpE,MAAM,MAAM,GAAgB;IAC1B,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE;IACnE,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE;IACvE,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE;CACnE,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAAkB,EAClB,MAAuB;IAEvB,MAAM,MAAM,GAA0B,EAAE,CAAC;IAEzC,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACzE,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;YAClF,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAuB,CAAC;YACpE,IAAI,CAAC,QAAQ;gBAAE,SAAS,CAAC,mCAAmC;YAE5D,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YACjF,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,QAAQ,CAAC,EAAE;oBACpB,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,SAAS,EAAE,KAAK,CAAC,QAAQ;oBACzB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,QAAQ;oBACR,MAAM,EAAE,gBAAgB;iBACzB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Pure markdown body/description formatters for Linear issues and projects.
3
+ *
4
+ * Every function here takes local OpenPlanr artifact data and returns the
5
+ * string that goes into the Linear entity's `description` / `title` field.
6
+ * Stateless + side-effect free except for `buildMergedTaskListBody`, which
7
+ * reads task files via the artifact-service to assemble a feature's
8
+ * aggregated checkbox body.
9
+ */
10
+ import { type ParsedSubtask } from '../../agents/task-parser.js';
11
+ import type { Epic, Feature, OpenPlanrConfig, UserStory } from '../../models/types.js';
12
+ /** Convert an unknown frontmatter value to an optional string at the type boundary. */
13
+ export declare function toOptionalString(v: unknown): string | undefined;
14
+ /** Convert an unknown frontmatter value to an optional array of strings. */
15
+ export declare function toOptionalStringArray(v: unknown): string[] | undefined;
16
+ /** Epic → Linear Project `description` (markdown). Skips whitespace-only sections. */
17
+ export declare function buildEpicProjectDescription(epic: Epic): string;
18
+ /** Feature → Linear issue body (overview + functional requirements bullets). */
19
+ export declare function buildFeatureIssueBody(feature: Feature): string;
20
+ /**
21
+ * User story → Linear sub-issue body.
22
+ *
23
+ * Composes, in order:
24
+ * 1. The "As a / I want / So that" sentence — only when all three fields
25
+ * are present (otherwise rendering with blanks produces visible
26
+ * garbage in Linear).
27
+ * 2. The frontmatter `acceptanceCriteria` prose — if set.
28
+ * 3. The Gherkin scenarios from `<storyId>-gherkin.feature` — if the
29
+ * caller provides them. Stories in the OpenPlanr convention store
30
+ * their real acceptance criteria as Gherkin in a sibling `.feature`
31
+ * file; without this, the Linear issue was empty for every story
32
+ * that followed the convention.
33
+ */
34
+ export declare function buildStoryIssueBody(story: UserStory, gherkinContent?: string | null): string;
35
+ /** Render parsed task lines to markdown checkboxes (Linear description). */
36
+ export declare function formatTaskCheckboxBody(parsed: ParsedSubtask[]): string;
37
+ /**
38
+ * Build a merged task-list body for a feature — concatenates every task
39
+ * artifact whose `featureId` matches, parses its checkboxes, renders them,
40
+ * and (when multiple files exist) prefixes each section with its task id
41
+ * as an `## h2`. Returns `''` when there's nothing to sync.
42
+ */
43
+ export declare function buildMergedTaskListBody(projectDir: string, config: OpenPlanrConfig, featureId: string, taskFiles: Array<{
44
+ id: string;
45
+ title: string;
46
+ }>): Promise<string>;
47
+ /**
48
+ * Extract the markdown body of a standalone artifact (QT / BL) for pushing
49
+ * to Linear as an issue description.
50
+ *
51
+ * Strips:
52
+ * - the frontmatter block (YAML between the `---` markers)
53
+ * - the top-level `# <ID>: <title>` heading (Linear shows the title
54
+ * separately, so repeating it in the description is noise)
55
+ *
56
+ * Everything else — prose, sub-headings, checkbox lists — is preserved
57
+ * verbatim. Linear renders standard markdown, so checkboxes stay checkboxes,
58
+ * `## sections` stay sections, links stay clickable.
59
+ */
60
+ export declare function buildStandaloneArtifactBody(raw: string, id: string): string;
61
+ /**
62
+ * Backlog item → Linear issue body. Priority + tags + description + optional
63
+ * acceptance criteria + notes. Accepts the generic frontmatter record shape
64
+ * because backlog items aren't currently loaded via a typed interface.
65
+ */
66
+ export declare function buildBacklogItemBody(bl: {
67
+ frontmatter: Record<string, unknown>;
68
+ }): string;
69
+ //# sourceMappingURL=body-formatters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"body-formatters.d.ts","sourceRoot":"","sources":["../../../src/services/linear/body-formatters.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAqB,MAAM,6BAA6B,CAAC;AACpF,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGvF,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE/D;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CAItE;AAED,sFAAsF;AACtF,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAc9D;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAS9D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAc5F;AAED,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAWtE;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GAC9C,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAgB3E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,MAAM,CAgBzF"}
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Pure markdown body/description formatters for Linear issues and projects.
3
+ *
4
+ * Every function here takes local OpenPlanr artifact data and returns the
5
+ * string that goes into the Linear entity's `description` / `title` field.
6
+ * Stateless + side-effect free except for `buildMergedTaskListBody`, which
7
+ * reads task files via the artifact-service to assemble a feature's
8
+ * aggregated checkbox body.
9
+ */
10
+ import { parseTaskMarkdown } from '../../agents/task-parser.js';
11
+ import { readArtifact, readArtifactRaw } from '../artifact-service.js';
12
+ /** Convert an unknown frontmatter value to an optional string at the type boundary. */
13
+ export function toOptionalString(v) {
14
+ return typeof v === 'string' ? v : undefined;
15
+ }
16
+ /** Convert an unknown frontmatter value to an optional array of strings. */
17
+ export function toOptionalStringArray(v) {
18
+ if (!Array.isArray(v))
19
+ return undefined;
20
+ const out = v.filter((item) => typeof item === 'string');
21
+ return out.length > 0 ? out : undefined;
22
+ }
23
+ /** Epic → Linear Project `description` (markdown). Skips whitespace-only sections. */
24
+ export function buildEpicProjectDescription(epic) {
25
+ const lines = [];
26
+ const section = (label, value) => {
27
+ const trimmed = value?.trim();
28
+ if (trimmed)
29
+ lines.push(`**${label}**\n\n${trimmed}`);
30
+ };
31
+ section('Business value', epic.businessValue);
32
+ section('Problem', epic.problemStatement);
33
+ section('Solution', epic.solutionOverview);
34
+ section('Success criteria', epic.successCriteria);
35
+ section('Target users', epic.targetUsers);
36
+ section('Risks', epic.risks);
37
+ section('Dependencies', epic.dependencies);
38
+ return lines.join('\n\n');
39
+ }
40
+ /** Feature → Linear issue body (overview + functional requirements bullets). */
41
+ export function buildFeatureIssueBody(feature) {
42
+ const lines = [feature.overview?.trim() || ''];
43
+ if (feature.functionalRequirements?.length) {
44
+ lines.push('**Functional requirements**');
45
+ for (const r of feature.functionalRequirements) {
46
+ lines.push(`- ${r}`);
47
+ }
48
+ }
49
+ return lines.filter(Boolean).join('\n\n');
50
+ }
51
+ /**
52
+ * User story → Linear sub-issue body.
53
+ *
54
+ * Composes, in order:
55
+ * 1. The "As a / I want / So that" sentence — only when all three fields
56
+ * are present (otherwise rendering with blanks produces visible
57
+ * garbage in Linear).
58
+ * 2. The frontmatter `acceptanceCriteria` prose — if set.
59
+ * 3. The Gherkin scenarios from `<storyId>-gherkin.feature` — if the
60
+ * caller provides them. Stories in the OpenPlanr convention store
61
+ * their real acceptance criteria as Gherkin in a sibling `.feature`
62
+ * file; without this, the Linear issue was empty for every story
63
+ * that followed the convention.
64
+ */
65
+ export function buildStoryIssueBody(story, gherkinContent) {
66
+ const role = story.role?.trim();
67
+ const goal = story.goal?.trim();
68
+ const benefit = story.benefit?.trim();
69
+ const ac = story.acceptanceCriteria?.trim();
70
+ const gherkin = gherkinContent?.trim();
71
+ const hasFullUserStoryLine = Boolean(role && goal && benefit);
72
+ const sections = [];
73
+ if (hasFullUserStoryLine) {
74
+ sections.push(`As a **${role}**, I want **${goal}** so that **${benefit}**.`);
75
+ }
76
+ if (ac)
77
+ sections.push(`**Acceptance criteria**\n\n${ac}`);
78
+ if (gherkin)
79
+ sections.push(`**Gherkin scenarios**\n\n\`\`\`gherkin\n${gherkin}\n\`\`\``);
80
+ return sections.join('\n\n');
81
+ }
82
+ /** Render parsed task lines to markdown checkboxes (Linear description). */
83
+ export function formatTaskCheckboxBody(parsed) {
84
+ if (parsed.length === 0)
85
+ return '';
86
+ return parsed
87
+ .map((p) => {
88
+ const mark = p.done ? 'x' : ' ';
89
+ if (p.depth === 0) {
90
+ return `- [${mark}] **${p.id}** ${p.title}`;
91
+ }
92
+ return ` - [${mark}] ${p.id} ${p.title}`;
93
+ })
94
+ .join('\n');
95
+ }
96
+ /**
97
+ * Build a merged task-list body for a feature — concatenates every task
98
+ * artifact whose `featureId` matches, parses its checkboxes, renders them,
99
+ * and (when multiple files exist) prefixes each section with its task id
100
+ * as an `## h2`. Returns `''` when there's nothing to sync.
101
+ */
102
+ export async function buildMergedTaskListBody(projectDir, config, featureId, taskFiles) {
103
+ const sections = [];
104
+ const sorted = [...taskFiles].sort((a, b) => a.id.localeCompare(b.id, undefined, { numeric: true }));
105
+ for (const tf of sorted) {
106
+ const raw = await readArtifactRaw(projectDir, config, 'task', tf.id);
107
+ if (!raw)
108
+ continue;
109
+ const data = (await readArtifact(projectDir, config, 'task', tf.id))?.data;
110
+ const fId = toOptionalString(data?.featureId);
111
+ if (fId !== featureId)
112
+ continue;
113
+ const parsed = parseTaskMarkdown(raw);
114
+ if (parsed.length === 0)
115
+ continue;
116
+ const body = formatTaskCheckboxBody(parsed);
117
+ if (taskFiles.length > 1) {
118
+ sections.push(`## ${tf.id}\n\n${body}`);
119
+ }
120
+ else {
121
+ sections.push(body);
122
+ }
123
+ }
124
+ return sections.join('\n\n');
125
+ }
126
+ /**
127
+ * Extract the markdown body of a standalone artifact (QT / BL) for pushing
128
+ * to Linear as an issue description.
129
+ *
130
+ * Strips:
131
+ * - the frontmatter block (YAML between the `---` markers)
132
+ * - the top-level `# <ID>: <title>` heading (Linear shows the title
133
+ * separately, so repeating it in the description is noise)
134
+ *
135
+ * Everything else — prose, sub-headings, checkbox lists — is preserved
136
+ * verbatim. Linear renders standard markdown, so checkboxes stay checkboxes,
137
+ * `## sections` stay sections, links stay clickable.
138
+ */
139
+ export function buildStandaloneArtifactBody(raw, id) {
140
+ // Strip frontmatter if present.
141
+ let body = raw;
142
+ const fmMatch = /^---[^\S\r\n]*\r?\n[\s\S]*?\r?\n---[^\S\r\n]*\r?\n?/.exec(raw);
143
+ if (fmMatch) {
144
+ body = raw.slice(fmMatch[0].length);
145
+ }
146
+ // Drop leading blank lines — markdown files typically have a blank line
147
+ // between the frontmatter's closing `---` and the first `#` heading.
148
+ body = body.replace(/^\s*\r?\n/, '').trimStart();
149
+ // Strip a single top-level `# <ID>:...` or `# <anything>` heading at the
150
+ // start of the body, plus the blank line that typically follows it.
151
+ const escapedId = id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
152
+ const titleHeadingRegex = new RegExp(`^#\\s+(?:${escapedId}:\\s*)?.*\\r?\\n(?:\\r?\\n)?`);
153
+ body = body.replace(titleHeadingRegex, '');
154
+ return body.trimEnd();
155
+ }
156
+ /**
157
+ * Backlog item → Linear issue body. Priority + tags + description + optional
158
+ * acceptance criteria + notes. Accepts the generic frontmatter record shape
159
+ * because backlog items aren't currently loaded via a typed interface.
160
+ */
161
+ export function buildBacklogItemBody(bl) {
162
+ const fm = bl.frontmatter;
163
+ const lines = [];
164
+ const priority = toOptionalString(fm.priority);
165
+ if (priority)
166
+ lines.push(`**Priority:** ${priority}`);
167
+ if (Array.isArray(fm.tags) && fm.tags.length > 0) {
168
+ const tags = fm.tags.filter((t) => typeof t === 'string');
169
+ if (tags.length)
170
+ lines.push(`**Tags:** ${tags.join(', ')}`);
171
+ }
172
+ const description = toOptionalString(fm.description);
173
+ if (description)
174
+ lines.push(description.trim());
175
+ const ac = toOptionalString(fm.acceptanceCriteria);
176
+ if (ac)
177
+ lines.push(`**Acceptance criteria**\n\n${ac.trim()}`);
178
+ const notes = toOptionalString(fm.notes);
179
+ if (notes)
180
+ lines.push(`**Notes**\n\n${notes.trim()}`);
181
+ return lines.join('\n\n');
182
+ }
183
+ //# sourceMappingURL=body-formatters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"body-formatters.js","sourceRoot":"","sources":["../../../src/services/linear/body-formatters.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Linear integration constants + id-shape validators + input-safety helpers.
3
+ *
4
+ * All field-limit values here are enforced at the SDK-wrapper layer in
5
+ * `src/services/linear-service.ts` so that every caller gets the guard for
6
+ * free. Shape validators (`isLikelyLinear*Id`) fence off stale frontmatter
7
+ * before it reaches the Linear API.
8
+ */
9
+ /** Credential-service provider key under which the Linear PAT is stored. */
10
+ export declare const LINEAR_CREDENTIAL_KEY: "linear";
11
+ /**
12
+ * Linear's backend enforces per-field length limits on every create/update
13
+ * mutation. Defend at the SDK-wrapper layer so callers don't have to think
14
+ * about them. Names / titles: confirmed or best-known caps; descriptions:
15
+ * conservative floors well under Linear's real ceilings (markdown + HTML
16
+ * are both accepted; real limits are in the tens of thousands).
17
+ */
18
+ export declare const LINEAR_FIELD_LIMITS: {
19
+ /** ProjectMilestone.name — confirmed 80 by `Argument Validation Error`. */
20
+ readonly milestoneName: 80;
21
+ /** IssueLabel.name — Linear team labels cap ~64 chars. */
22
+ readonly labelName: 64;
23
+ /** Project.name — generous cap. */
24
+ readonly projectName: 256;
25
+ /** Issue.title — Linear issue title cap ~255. */
26
+ readonly issueTitle: 255;
27
+ /** Project.description — conservative floor; real Linear ceiling is higher. */
28
+ readonly projectDescription: 50000;
29
+ /** ProjectMilestone.description. */
30
+ readonly milestoneDescription: 50000;
31
+ /** IssueLabel.description — labels rarely need long descriptions. */
32
+ readonly labelDescription: 500;
33
+ /** Issue.description (markdown body). */
34
+ readonly issueDescription: 65000;
35
+ };
36
+ /**
37
+ * Truncate a string to Linear's character limit for a given field. Logs a
38
+ * warning on truncation so the operator can spot it in the push output.
39
+ */
40
+ export declare function truncateForLinear(value: string, maxLen: number, fieldLabel: string): string;
41
+ /**
42
+ * Non-empty guard for required Linear name/title fields. Fails fast with an
43
+ * actionable message before the API would reject the call.
44
+ */
45
+ export declare function requireNonEmpty(value: string | null | undefined, fieldLabel: string): string;
46
+ /**
47
+ * Heuristic: Linear workflow state id (uuid) vs human-readable state name.
48
+ * The `/i` flag is intentional — Linear's API canonicalizes UUIDs to
49
+ * lowercase, but defensive acceptance of uppercase hex matches RFC 4122
50
+ * and protects against tools that normalize differently.
51
+ */
52
+ export declare function isLikelyLinearWorkflowStateId(s: string): boolean;
53
+ /**
54
+ * Validate that a value plausibly identifies a Linear issue. Two valid shapes:
55
+ * 1. UUIDv4 (e.g. `9b2f4c3e-...`) — canonical API form
56
+ * 2. Linear identifier (e.g. `ENG-42`) — human-readable, also accepted by `client.issue()`
57
+ * Anything else is treated as stale/corrupted frontmatter and skipped before
58
+ * hitting the API.
59
+ */
60
+ export declare function isLikelyLinearIssueId(s: string): boolean;
61
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/services/linear/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,4EAA4E;AAC5E,eAAO,MAAM,qBAAqB,EAAG,QAAiB,CAAC;AAEvD;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;IAC9B,2EAA2E;;IAE3E,0DAA0D;;IAE1D,mCAAmC;;IAEnC,iDAAiD;;IAEjD,+EAA+E;;IAE/E,oCAAoC;;IAEpC,qEAAqE;;IAErE,yCAAyC;;CAEjC,CAAC;AAEX;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ3F;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ5F;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAIhE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAKxD"}