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.
- package/README.md +41 -4
- package/dist/agents/task-parser.d.ts.map +1 -1
- package/dist/agents/task-parser.js +8 -34
- package/dist/agents/task-parser.js.map +1 -1
- package/dist/ai/prompts/prompt-builder.d.ts +48 -0
- package/dist/ai/prompts/prompt-builder.d.ts.map +1 -1
- package/dist/ai/prompts/prompt-builder.js +57 -1
- package/dist/ai/prompts/prompt-builder.js.map +1 -1
- package/dist/ai/prompts/system-prompts.d.ts +24 -1
- package/dist/ai/prompts/system-prompts.d.ts.map +1 -1
- package/dist/ai/prompts/system-prompts.js +104 -6
- package/dist/ai/prompts/system-prompts.js.map +1 -1
- package/dist/ai/schemas/ai-response-schemas.d.ts +68 -0
- package/dist/ai/schemas/ai-response-schemas.d.ts.map +1 -1
- package/dist/ai/schemas/ai-response-schemas.js +81 -0
- package/dist/ai/schemas/ai-response-schemas.js.map +1 -1
- package/dist/ai/types.d.ts +2 -0
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/ai/types.js +4 -0
- package/dist/ai/types.js.map +1 -1
- package/dist/cli/commands/backlog.d.ts +12 -0
- package/dist/cli/commands/backlog.d.ts.map +1 -1
- package/dist/cli/commands/backlog.js +88 -2
- package/dist/cli/commands/backlog.js.map +1 -1
- package/dist/cli/commands/config.d.ts.map +1 -1
- package/dist/cli/commands/config.js +8 -2
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/linear.d.ts +8 -0
- package/dist/cli/commands/linear.d.ts.map +1 -0
- package/dist/cli/commands/linear.js +550 -0
- package/dist/cli/commands/linear.js.map +1 -0
- package/dist/cli/commands/quick.d.ts +17 -0
- package/dist/cli/commands/quick.d.ts.map +1 -1
- package/dist/cli/commands/quick.js +31 -15
- package/dist/cli/commands/quick.js.map +1 -1
- package/dist/cli/commands/revise.d.ts +24 -0
- package/dist/cli/commands/revise.d.ts.map +1 -0
- package/dist/cli/commands/revise.js +570 -0
- package/dist/cli/commands/revise.js.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/models/schema.d.ts +43 -0
- package/dist/models/schema.d.ts.map +1 -1
- package/dist/models/schema.js +49 -0
- package/dist/models/schema.js.map +1 -1
- package/dist/models/types.d.ts +296 -0
- package/dist/models/types.d.ts.map +1 -1
- package/dist/services/artifact-gathering.d.ts +4 -0
- package/dist/services/artifact-gathering.d.ts.map +1 -1
- package/dist/services/artifact-gathering.js +1 -1
- package/dist/services/artifact-gathering.js.map +1 -1
- package/dist/services/artifact-service.d.ts +12 -1
- package/dist/services/artifact-service.d.ts.map +1 -1
- package/dist/services/artifact-service.js +49 -6
- package/dist/services/artifact-service.js.map +1 -1
- package/dist/services/atomic-write-service.d.ts +41 -0
- package/dist/services/atomic-write-service.d.ts.map +1 -0
- package/dist/services/atomic-write-service.js +87 -0
- package/dist/services/atomic-write-service.js.map +1 -0
- package/dist/services/audit-log-service.d.ts +47 -0
- package/dist/services/audit-log-service.d.ts.map +1 -0
- package/dist/services/audit-log-service.js +210 -0
- package/dist/services/audit-log-service.js.map +1 -0
- package/dist/services/cascade-service.d.ts +62 -0
- package/dist/services/cascade-service.d.ts.map +1 -0
- package/dist/services/cascade-service.js +189 -0
- package/dist/services/cascade-service.js.map +1 -0
- package/dist/services/credentials-service.js +2 -2
- package/dist/services/credentials-service.js.map +1 -1
- package/dist/services/diff-service.d.ts +18 -0
- package/dist/services/diff-service.d.ts.map +1 -0
- package/dist/services/diff-service.js +35 -0
- package/dist/services/diff-service.js.map +1 -0
- package/dist/services/evidence-verifier.d.ts +71 -0
- package/dist/services/evidence-verifier.d.ts.map +1 -0
- package/dist/services/evidence-verifier.js +174 -0
- package/dist/services/evidence-verifier.js.map +1 -0
- package/dist/services/git-service.d.ts +60 -0
- package/dist/services/git-service.d.ts.map +1 -0
- package/dist/services/git-service.js +137 -0
- package/dist/services/git-service.js.map +1 -0
- package/dist/services/graph-integrity.d.ts +35 -0
- package/dist/services/graph-integrity.d.ts.map +1 -0
- package/dist/services/graph-integrity.js +53 -0
- package/dist/services/graph-integrity.js.map +1 -0
- package/dist/services/linear/body-formatters.d.ts +69 -0
- package/dist/services/linear/body-formatters.d.ts.map +1 -0
- package/dist/services/linear/body-formatters.js +183 -0
- package/dist/services/linear/body-formatters.js.map +1 -0
- package/dist/services/linear/constants.d.ts +61 -0
- package/dist/services/linear/constants.d.ts.map +1 -0
- package/dist/services/linear/constants.js +84 -0
- package/dist/services/linear/constants.js.map +1 -0
- package/dist/services/linear/errors.d.ts +14 -0
- package/dist/services/linear/errors.d.ts.map +1 -0
- package/dist/services/linear/errors.js +106 -0
- package/dist/services/linear/errors.js.map +1 -0
- package/dist/services/linear/estimate-resolver.d.ts +50 -0
- package/dist/services/linear/estimate-resolver.d.ts.map +1 -0
- package/dist/services/linear/estimate-resolver.js +82 -0
- package/dist/services/linear/estimate-resolver.js.map +1 -0
- package/dist/services/linear/plan-builders.d.ts +64 -0
- package/dist/services/linear/plan-builders.d.ts.map +1 -0
- package/dist/services/linear/plan-builders.js +237 -0
- package/dist/services/linear/plan-builders.js.map +1 -0
- package/dist/services/linear/scope-loaders.d.ts +79 -0
- package/dist/services/linear/scope-loaders.d.ts.map +1 -0
- package/dist/services/linear/scope-loaders.js +227 -0
- package/dist/services/linear/scope-loaders.js.map +1 -0
- package/dist/services/linear/strategy-context.d.ts +66 -0
- package/dist/services/linear/strategy-context.d.ts.map +1 -0
- package/dist/services/linear/strategy-context.js +121 -0
- package/dist/services/linear/strategy-context.js.map +1 -0
- package/dist/services/linear-mapping-service.d.ts +11 -0
- package/dist/services/linear-mapping-service.d.ts.map +1 -0
- package/dist/services/linear-mapping-service.js +220 -0
- package/dist/services/linear-mapping-service.js.map +1 -0
- package/dist/services/linear-pull-service.d.ts +137 -0
- package/dist/services/linear-pull-service.d.ts.map +1 -0
- package/dist/services/linear-pull-service.js +720 -0
- package/dist/services/linear-pull-service.js.map +1 -0
- package/dist/services/linear-push-service.d.ts +86 -0
- package/dist/services/linear-push-service.d.ts.map +1 -0
- package/dist/services/linear-push-service.js +956 -0
- package/dist/services/linear-push-service.js.map +1 -0
- package/dist/services/linear-service.d.ts +122 -0
- package/dist/services/linear-service.d.ts.map +1 -0
- package/dist/services/linear-service.js +361 -0
- package/dist/services/linear-service.js.map +1 -0
- package/dist/services/prompt-service.d.ts +37 -0
- package/dist/services/prompt-service.d.ts.map +1 -1
- package/dist/services/prompt-service.js +111 -0
- package/dist/services/prompt-service.js.map +1 -1
- package/dist/services/revise-apply-service.d.ts +55 -0
- package/dist/services/revise-apply-service.d.ts.map +1 -0
- package/dist/services/revise-apply-service.js +255 -0
- package/dist/services/revise-apply-service.js.map +1 -0
- package/dist/services/revise-cache-service.d.ts +46 -0
- package/dist/services/revise-cache-service.d.ts.map +1 -0
- package/dist/services/revise-cache-service.js +88 -0
- package/dist/services/revise-cache-service.js.map +1 -0
- package/dist/services/revise-plan-service.d.ts +38 -0
- package/dist/services/revise-plan-service.d.ts.map +1 -0
- package/dist/services/revise-plan-service.js +151 -0
- package/dist/services/revise-plan-service.js.map +1 -0
- package/dist/services/revise-service.d.ts +115 -0
- package/dist/services/revise-service.d.ts.map +1 -0
- package/dist/services/revise-service.js +294 -0
- package/dist/services/revise-service.js.map +1 -0
- package/dist/services/template-sections.d.ts +28 -0
- package/dist/services/template-sections.d.ts.map +1 -0
- package/dist/services/template-sections.js +55 -0
- package/dist/services/template-sections.js.map +1 -0
- package/dist/templates/backlog/backlog-item.md.hbs +3 -0
- package/dist/templates/quick/quick-task.md.hbs +6 -0
- package/dist/utils/diff.d.ts +47 -0
- package/dist/utils/diff.d.ts.map +1 -0
- package/dist/utils/diff.js +278 -0
- package/dist/utils/diff.js.map +1 -0
- package/dist/utils/markdown.d.ts +23 -0
- package/dist/utils/markdown.d.ts.map +1 -1
- package/dist/utils/markdown.js +79 -0
- package/dist/utils/markdown.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
import { logger } from '../../utils/logger.js';
|
|
10
|
+
/** Credential-service provider key under which the Linear PAT is stored. */
|
|
11
|
+
export const LINEAR_CREDENTIAL_KEY = 'linear';
|
|
12
|
+
/**
|
|
13
|
+
* Linear's backend enforces per-field length limits on every create/update
|
|
14
|
+
* mutation. Defend at the SDK-wrapper layer so callers don't have to think
|
|
15
|
+
* about them. Names / titles: confirmed or best-known caps; descriptions:
|
|
16
|
+
* conservative floors well under Linear's real ceilings (markdown + HTML
|
|
17
|
+
* are both accepted; real limits are in the tens of thousands).
|
|
18
|
+
*/
|
|
19
|
+
export const LINEAR_FIELD_LIMITS = {
|
|
20
|
+
/** ProjectMilestone.name — confirmed 80 by `Argument Validation Error`. */
|
|
21
|
+
milestoneName: 80,
|
|
22
|
+
/** IssueLabel.name — Linear team labels cap ~64 chars. */
|
|
23
|
+
labelName: 64,
|
|
24
|
+
/** Project.name — generous cap. */
|
|
25
|
+
projectName: 256,
|
|
26
|
+
/** Issue.title — Linear issue title cap ~255. */
|
|
27
|
+
issueTitle: 255,
|
|
28
|
+
/** Project.description — conservative floor; real Linear ceiling is higher. */
|
|
29
|
+
projectDescription: 50_000,
|
|
30
|
+
/** ProjectMilestone.description. */
|
|
31
|
+
milestoneDescription: 50_000,
|
|
32
|
+
/** IssueLabel.description — labels rarely need long descriptions. */
|
|
33
|
+
labelDescription: 500,
|
|
34
|
+
/** Issue.description (markdown body). */
|
|
35
|
+
issueDescription: 65_000,
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Truncate a string to Linear's character limit for a given field. Logs a
|
|
39
|
+
* warning on truncation so the operator can spot it in the push output.
|
|
40
|
+
*/
|
|
41
|
+
export function truncateForLinear(value, maxLen, fieldLabel) {
|
|
42
|
+
const trimmed = value.trim();
|
|
43
|
+
if (trimmed.length <= maxLen)
|
|
44
|
+
return trimmed;
|
|
45
|
+
const truncated = trimmed.slice(0, maxLen);
|
|
46
|
+
logger.warn(`${fieldLabel} truncated from ${trimmed.length} → ${maxLen} chars to satisfy Linear's limit.`);
|
|
47
|
+
return truncated;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Non-empty guard for required Linear name/title fields. Fails fast with an
|
|
51
|
+
* actionable message before the API would reject the call.
|
|
52
|
+
*/
|
|
53
|
+
export function requireNonEmpty(value, fieldLabel) {
|
|
54
|
+
const trimmed = typeof value === 'string' ? value.trim() : '';
|
|
55
|
+
if (!trimmed) {
|
|
56
|
+
throw new Error(`${fieldLabel} is empty — Linear requires a non-empty value. Add a title to the OpenPlanr artifact and re-run.`);
|
|
57
|
+
}
|
|
58
|
+
return trimmed;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Heuristic: Linear workflow state id (uuid) vs human-readable state name.
|
|
62
|
+
* The `/i` flag is intentional — Linear's API canonicalizes UUIDs to
|
|
63
|
+
* lowercase, but defensive acceptance of uppercase hex matches RFC 4122
|
|
64
|
+
* and protects against tools that normalize differently.
|
|
65
|
+
*/
|
|
66
|
+
export function isLikelyLinearWorkflowStateId(s) {
|
|
67
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(s.trim());
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Validate that a value plausibly identifies a Linear issue. Two valid shapes:
|
|
71
|
+
* 1. UUIDv4 (e.g. `9b2f4c3e-...`) — canonical API form
|
|
72
|
+
* 2. Linear identifier (e.g. `ENG-42`) — human-readable, also accepted by `client.issue()`
|
|
73
|
+
* Anything else is treated as stale/corrupted frontmatter and skipped before
|
|
74
|
+
* hitting the API.
|
|
75
|
+
*/
|
|
76
|
+
export function isLikelyLinearIssueId(s) {
|
|
77
|
+
const trimmed = s.trim();
|
|
78
|
+
if (trimmed.length === 0)
|
|
79
|
+
return false;
|
|
80
|
+
if (isLikelyLinearWorkflowStateId(trimmed))
|
|
81
|
+
return true;
|
|
82
|
+
return /^[A-Z]{2,}-\d+$/.test(trimmed);
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/services/linear/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE/C,4EAA4E;AAC5E,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAiB,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,2EAA2E;IAC3E,aAAa,EAAE,EAAE;IACjB,0DAA0D;IAC1D,SAAS,EAAE,EAAE;IACb,mCAAmC;IACnC,WAAW,EAAE,GAAG;IAChB,iDAAiD;IACjD,UAAU,EAAE,GAAG;IACf,+EAA+E;IAC/E,kBAAkB,EAAE,MAAM;IAC1B,oCAAoC;IACpC,oBAAoB,EAAE,MAAM;IAC5B,qEAAqE;IACrE,gBAAgB,EAAE,GAAG;IACrB,yCAAyC;IACzC,gBAAgB,EAAE,MAAM;CAChB,CAAC;AAEX;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,MAAc,EAAE,UAAkB;IACjF,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,OAAO,CAAC;IAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC3C,MAAM,CAAC,IAAI,CACT,GAAG,UAAU,mBAAmB,OAAO,CAAC,MAAM,MAAM,MAAM,mCAAmC,CAC9F,CAAC;IACF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAgC,EAAE,UAAkB;IAClF,MAAM,OAAO,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,GAAG,UAAU,kGAAkG,CAChH,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,CAAS;IACrD,OAAO,4EAA4E,CAAC,IAAI,CACtF,CAAC,CAAC,IAAI,EAAE,CACT,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,CAAS;IAC7C,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,IAAI,6BAA6B,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACxD,OAAO,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linear error classification, user-facing mapping, and retry wrapper.
|
|
3
|
+
*
|
|
4
|
+
* Two responsibilities:
|
|
5
|
+
* 1. Map raw SDK errors to user-friendly messages (branded guidance for
|
|
6
|
+
* auth / network / rate-limit; surfaced friendly SDK message for other
|
|
7
|
+
* classified types; sanitized fallback for unclassified errors).
|
|
8
|
+
* 2. Wrap Linear calls with small exponential backoff that honours
|
|
9
|
+
* `RatelimitedLinearError.retryAfter`.
|
|
10
|
+
*/
|
|
11
|
+
/** Wraps a Linear call with small exponential backoff on rate limit / network errors. */
|
|
12
|
+
export declare function withLinearRetry<T>(op: string, fn: () => Promise<T>, retries?: number): Promise<T>;
|
|
13
|
+
export declare function mapLinearError(err: unknown, context: string): Error;
|
|
14
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/services/linear/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAgCH,yFAAyF;AACzF,wBAAsB,eAAe,CAAC,CAAC,EACrC,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE,MAAwB,GAChC,OAAO,CAAC,CAAC,CAAC,CA4BZ;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CA6CnE"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linear error classification, user-facing mapping, and retry wrapper.
|
|
3
|
+
*
|
|
4
|
+
* Two responsibilities:
|
|
5
|
+
* 1. Map raw SDK errors to user-friendly messages (branded guidance for
|
|
6
|
+
* auth / network / rate-limit; surfaced friendly SDK message for other
|
|
7
|
+
* classified types; sanitized fallback for unclassified errors).
|
|
8
|
+
* 2. Wrap Linear calls with small exponential backoff that honours
|
|
9
|
+
* `RatelimitedLinearError.retryAfter`.
|
|
10
|
+
*/
|
|
11
|
+
import { LinearError, LinearErrorType, RatelimitedLinearError } from '@linear/sdk';
|
|
12
|
+
import { logger } from '../../utils/logger.js';
|
|
13
|
+
const DEFAULT_RETRIES = 3;
|
|
14
|
+
/**
|
|
15
|
+
* Extract the first user-facing message from a LinearError. The SDK exposes
|
|
16
|
+
* per-GraphQL-error `.message` strings on `errors[]` — these are backend-
|
|
17
|
+
* authored user descriptions (e.g., "Milestone already exists with this
|
|
18
|
+
* name"). Falls back to `err.message` and finally to the error `type`.
|
|
19
|
+
* Safe to surface — this is not the raw query/variables.
|
|
20
|
+
*/
|
|
21
|
+
function extractLinearFriendlyMessage(err) {
|
|
22
|
+
const first = err.errors?.[0]?.message?.trim();
|
|
23
|
+
if (first)
|
|
24
|
+
return first;
|
|
25
|
+
const top = err.message?.trim();
|
|
26
|
+
if (top)
|
|
27
|
+
return top;
|
|
28
|
+
const type = err.type;
|
|
29
|
+
return type || undefined;
|
|
30
|
+
}
|
|
31
|
+
function isRetriableLinearError(err) {
|
|
32
|
+
if (err instanceof LinearError) {
|
|
33
|
+
const t = err.type ?? LinearErrorType.Unknown;
|
|
34
|
+
return t === LinearErrorType.Ratelimited || t === LinearErrorType.NetworkError;
|
|
35
|
+
}
|
|
36
|
+
if (err instanceof Error && err.name === 'AbortError')
|
|
37
|
+
return true;
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
/** Wraps a Linear call with small exponential backoff on rate limit / network errors. */
|
|
41
|
+
export async function withLinearRetry(op, fn, retries = DEFAULT_RETRIES) {
|
|
42
|
+
let last;
|
|
43
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
44
|
+
try {
|
|
45
|
+
return await fn();
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
last = err;
|
|
49
|
+
if (attempt < retries && isRetriableLinearError(err)) {
|
|
50
|
+
// Prefer Linear's own `Retry-After` when the error is a rate-limit
|
|
51
|
+
// (surfaced on the `RatelimitedLinearError` subclass as a seconds
|
|
52
|
+
// value). Fall back to exponential backoff for network errors and
|
|
53
|
+
// when the server didn't advertise a retry hint. Use `Math.max` so
|
|
54
|
+
// we respect both: never retry sooner than Linear asked, never
|
|
55
|
+
// faster than our own backoff schedule.
|
|
56
|
+
const retryAfterMs = err instanceof RatelimitedLinearError && typeof err.retryAfter === 'number'
|
|
57
|
+
? Math.max(0, err.retryAfter) * 1000
|
|
58
|
+
: 0;
|
|
59
|
+
const backoffMs = Math.min(30_000, 500 * 2 ** attempt);
|
|
60
|
+
const waitMs = Math.max(retryAfterMs, backoffMs);
|
|
61
|
+
logger.dim(`Linear ${op}: retrying in ${waitMs}ms (attempt ${attempt + 1}/${retries})...`);
|
|
62
|
+
await new Promise((r) => setTimeout(r, waitMs));
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
throw mapLinearError(err, op);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
throw mapLinearError(last, op);
|
|
69
|
+
}
|
|
70
|
+
export function mapLinearError(err, context) {
|
|
71
|
+
if (err instanceof Error && err.name === 'AbortError') {
|
|
72
|
+
return new Error(`Network error while ${context}: request was cancelled or timed out.`);
|
|
73
|
+
}
|
|
74
|
+
if (err instanceof LinearError) {
|
|
75
|
+
const t = err.type ?? LinearErrorType.Unknown;
|
|
76
|
+
if (t === LinearErrorType.AuthenticationError) {
|
|
77
|
+
return new Error(`Linear rejected this token while ${context}. Create a new PAT at https://linear.app/settings/account/security (app, read, write as needed) and run \`planr linear init\` again.`);
|
|
78
|
+
}
|
|
79
|
+
if (t === LinearErrorType.NetworkError) {
|
|
80
|
+
return new Error(`Cannot reach Linear while ${context}. Check your network connection, try again, and see https://status.linear.app for outages.`);
|
|
81
|
+
}
|
|
82
|
+
if (t === LinearErrorType.Ratelimited) {
|
|
83
|
+
return new Error('Linear rate limit reached. Wait about 1–2 minutes (longer if you are polling heavily), then retry. See https://status.linear.app if issues persist.');
|
|
84
|
+
}
|
|
85
|
+
// For other classified LinearError types (Forbidden, InvalidInput, UserError,
|
|
86
|
+
// FeatureNotAccessible, Internal, LockTimeout, UsageLimitExceeded, Graphql,
|
|
87
|
+
// Other, Bootstrap, Unknown) surface the SDK's friendly .message string —
|
|
88
|
+
// backend-authored user descriptions like "Milestone name already exists
|
|
89
|
+
// in project" or "Input invalid: projectId". Also log the full object at
|
|
90
|
+
// debug level for --verbose diagnostics.
|
|
91
|
+
logger.debug(`Linear error (${context}, type=${t})`, err);
|
|
92
|
+
const friendly = extractLinearFriendlyMessage(err);
|
|
93
|
+
const suffix = friendly ? `: ${friendly}` : '';
|
|
94
|
+
if (t === LinearErrorType.Forbidden) {
|
|
95
|
+
return new Error(`Permission denied while ${context}${suffix}. Your token may be missing the required scope, or your user cannot access this resource.`);
|
|
96
|
+
}
|
|
97
|
+
return new Error(`Linear error while ${context} (${t})${suffix}`);
|
|
98
|
+
}
|
|
99
|
+
// Unknown / unclassified error class: log the full object at debug level so
|
|
100
|
+
// operators can inspect it with `--verbose`, but do NOT surface the raw
|
|
101
|
+
// message to end users — could contain arbitrary response bodies.
|
|
102
|
+
logger.debug(`Linear error (${context})`, err);
|
|
103
|
+
const klass = err instanceof Error ? err.constructor.name : 'Unknown';
|
|
104
|
+
return new Error(`Linear error while ${context} (${klass}). Re-run with --verbose for diagnostic details.`);
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/services/linear/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE/C,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B;;;;;;GAMG;AACH,SAAS,4BAA4B,CAAC,GAAgB;IACpD,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;IAChC,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACpB,MAAM,IAAI,GAAI,GAAyB,CAAC,IAAI,CAAC;IAC7C,OAAO,IAAI,IAAI,SAAS,CAAC;AAC3B,CAAC;AAED,SAAS,sBAAsB,CAAC,GAAY;IAC1C,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAI,GAAyB,CAAC,IAAI,IAAI,eAAe,CAAC,OAAO,CAAC;QACrE,OAAO,CAAC,KAAK,eAAe,CAAC,WAAW,IAAI,CAAC,KAAK,eAAe,CAAC,YAAY,CAAC;IACjF,CAAC;IACD,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IACnE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,yFAAyF;AACzF,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,EAAU,EACV,EAAoB,EACpB,UAAkB,eAAe;IAEjC,IAAI,IAAa,CAAC;IAClB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,GAAG,CAAC;YACX,IAAI,OAAO,GAAG,OAAO,IAAI,sBAAsB,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrD,mEAAmE;gBACnE,kEAAkE;gBAClE,kEAAkE;gBAClE,mEAAmE;gBACnE,+DAA+D;gBAC/D,wCAAwC;gBACxC,MAAM,YAAY,GAChB,GAAG,YAAY,sBAAsB,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ;oBACzE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI;oBACpC,CAAC,CAAC,CAAC,CAAC;gBACR,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC;gBACvD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;gBACjD,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,iBAAiB,MAAM,eAAe,OAAO,GAAG,CAAC,IAAI,OAAO,MAAM,CAAC,CAAC;gBAC3F,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;gBAChD,SAAS;YACX,CAAC;YACD,MAAM,cAAc,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IACD,MAAM,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAY,EAAE,OAAe;IAC1D,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QACtD,OAAO,IAAI,KAAK,CAAC,uBAAuB,OAAO,uCAAuC,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAI,GAAyB,CAAC,IAAI,IAAI,eAAe,CAAC,OAAO,CAAC;QACrE,IAAI,CAAC,KAAK,eAAe,CAAC,mBAAmB,EAAE,CAAC;YAC9C,OAAO,IAAI,KAAK,CACd,oCAAoC,OAAO,sIAAsI,CAClL,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,KAAK,eAAe,CAAC,YAAY,EAAE,CAAC;YACvC,OAAO,IAAI,KAAK,CACd,6BAA6B,OAAO,4FAA4F,CACjI,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,KAAK,eAAe,CAAC,WAAW,EAAE,CAAC;YACtC,OAAO,IAAI,KAAK,CACd,qJAAqJ,CACtJ,CAAC;QACJ,CAAC;QACD,8EAA8E;QAC9E,4EAA4E;QAC5E,0EAA0E;QAC1E,yEAAyE;QACzE,yEAAyE;QACzE,yCAAyC;QACzC,MAAM,CAAC,KAAK,CAAC,iBAAiB,OAAO,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,4BAA4B,CAAC,GAAG,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,IAAI,CAAC,KAAK,eAAe,CAAC,SAAS,EAAE,CAAC;YACpC,OAAO,IAAI,KAAK,CACd,2BAA2B,OAAO,GAAG,MAAM,2FAA2F,CACvI,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,KAAK,CAAC,sBAAsB,OAAO,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,4EAA4E;IAC5E,wEAAwE;IACxE,kEAAkE;IAClE,MAAM,CAAC,KAAK,CAAC,iBAAiB,OAAO,GAAG,EAAE,GAAG,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,OAAO,IAAI,KAAK,CACd,sBAAsB,OAAO,KAAK,KAAK,kDAAkD,CAC1F,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scale-aware snapping from OpenPlanr `storyPoints` → Linear `estimate` field.
|
|
3
|
+
*
|
|
4
|
+
* Linear's estimate field is numeric and must match the team's configured
|
|
5
|
+
* scale (`fibonacci`, `linear`, `exponential`, or `tShirt`). A value that
|
|
6
|
+
* isn't on the scale is rejected by the API, so we snap to the nearest
|
|
7
|
+
* allowed value before sending. Callers use the returned snap event to emit
|
|
8
|
+
* a debug log once per transformation.
|
|
9
|
+
*
|
|
10
|
+
* Scales mirror Linear's own SDK values — see `LinearIssueEstimationType`
|
|
11
|
+
* in `src/models/types.ts`.
|
|
12
|
+
*/
|
|
13
|
+
import type { LinearIssueEstimationType } from '../../models/types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Result of resolving a local estimate for a push. Exactly one of
|
|
16
|
+
* `estimate` (mapped value, ready to send to Linear) or `reason` (why the
|
|
17
|
+
* field is being omitted) is populated.
|
|
18
|
+
*/
|
|
19
|
+
export type EstimateResolution = {
|
|
20
|
+
kind: 'mapped';
|
|
21
|
+
estimate: number;
|
|
22
|
+
originalValue: number;
|
|
23
|
+
snapped: boolean;
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'skip-not-used';
|
|
26
|
+
} | {
|
|
27
|
+
kind: 'skip-t-shirt';
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'skip-no-local-value';
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'skip-invalid-value';
|
|
32
|
+
rawValue: unknown;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a local `storyPoints` (or `estimatedPoints`) value to a Linear
|
|
36
|
+
* `estimate` value given the team's scale.
|
|
37
|
+
*
|
|
38
|
+
* Precedence for the raw local value:
|
|
39
|
+
* 1. `frontmatter.estimatedPoints` — canonical name written by
|
|
40
|
+
* `planr estimate --save` (see `src/cli/commands/estimate.ts`).
|
|
41
|
+
* 2. `frontmatter.storyPoints` — alias accepted for hand-edited files or
|
|
42
|
+
* direct AI-response copies that used the schema field name verbatim.
|
|
43
|
+
*
|
|
44
|
+
* Returns one of:
|
|
45
|
+
* - `mapped` — include `estimate: <value>` in the push input
|
|
46
|
+
* - `skip-*` — omit the `estimate` field; `kind` carries the reason for
|
|
47
|
+
* logging / dry-run display
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveEstimateForPush(frontmatter: Record<string, unknown>, scale: LinearIssueEstimationType | string | undefined): EstimateResolution;
|
|
50
|
+
//# sourceMappingURL=estimate-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"estimate-resolver.d.ts","sourceRoot":"","sources":["../../../src/services/linear/estimate-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAqBvE;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC7E;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,qBAAqB,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,KAAK,EAAE,yBAAyB,GAAG,MAAM,GAAG,SAAS,GACpD,kBAAkB,CAyCpB"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scale-aware snapping from OpenPlanr `storyPoints` → Linear `estimate` field.
|
|
3
|
+
*
|
|
4
|
+
* Linear's estimate field is numeric and must match the team's configured
|
|
5
|
+
* scale (`fibonacci`, `linear`, `exponential`, or `tShirt`). A value that
|
|
6
|
+
* isn't on the scale is rejected by the API, so we snap to the nearest
|
|
7
|
+
* allowed value before sending. Callers use the returned snap event to emit
|
|
8
|
+
* a debug log once per transformation.
|
|
9
|
+
*
|
|
10
|
+
* Scales mirror Linear's own SDK values — see `LinearIssueEstimationType`
|
|
11
|
+
* in `src/models/types.ts`.
|
|
12
|
+
*/
|
|
13
|
+
const FIBONACCI_SCALE = [0, 1, 2, 3, 5, 8, 13, 21];
|
|
14
|
+
const LINEAR_SCALE = [0, 1, 2, 3, 4, 5];
|
|
15
|
+
const EXPONENTIAL_SCALE = [0, 1, 2, 4, 8, 16];
|
|
16
|
+
function snapToNearest(value, scale) {
|
|
17
|
+
let best = scale[0];
|
|
18
|
+
let bestDistance = Math.abs(value - best);
|
|
19
|
+
for (const candidate of scale) {
|
|
20
|
+
const distance = Math.abs(value - candidate);
|
|
21
|
+
// Break ties toward the larger value — under-estimating is a common
|
|
22
|
+
// planning bias; snapping up ("4 is really a 5") leans against it.
|
|
23
|
+
if (distance < bestDistance || (distance === bestDistance && candidate > best)) {
|
|
24
|
+
best = candidate;
|
|
25
|
+
bestDistance = distance;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return best;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a local `storyPoints` (or `estimatedPoints`) value to a Linear
|
|
32
|
+
* `estimate` value given the team's scale.
|
|
33
|
+
*
|
|
34
|
+
* Precedence for the raw local value:
|
|
35
|
+
* 1. `frontmatter.estimatedPoints` — canonical name written by
|
|
36
|
+
* `planr estimate --save` (see `src/cli/commands/estimate.ts`).
|
|
37
|
+
* 2. `frontmatter.storyPoints` — alias accepted for hand-edited files or
|
|
38
|
+
* direct AI-response copies that used the schema field name verbatim.
|
|
39
|
+
*
|
|
40
|
+
* Returns one of:
|
|
41
|
+
* - `mapped` — include `estimate: <value>` in the push input
|
|
42
|
+
* - `skip-*` — omit the `estimate` field; `kind` carries the reason for
|
|
43
|
+
* logging / dry-run display
|
|
44
|
+
*/
|
|
45
|
+
export function resolveEstimateForPush(frontmatter, scale) {
|
|
46
|
+
if (scale === 'notUsed' || !scale) {
|
|
47
|
+
return { kind: 'skip-not-used' };
|
|
48
|
+
}
|
|
49
|
+
if (scale === 'tShirt') {
|
|
50
|
+
// No safe numeric → XS/S/M/L/XL mapping without user config. Skip.
|
|
51
|
+
return { kind: 'skip-t-shirt' };
|
|
52
|
+
}
|
|
53
|
+
const rawLocal = frontmatter.estimatedPoints !== undefined
|
|
54
|
+
? frontmatter.estimatedPoints
|
|
55
|
+
: frontmatter.storyPoints;
|
|
56
|
+
if (rawLocal === undefined || rawLocal === null || rawLocal === '') {
|
|
57
|
+
return { kind: 'skip-no-local-value' };
|
|
58
|
+
}
|
|
59
|
+
const parsed = typeof rawLocal === 'number' ? rawLocal : Number(rawLocal);
|
|
60
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
61
|
+
return { kind: 'skip-invalid-value', rawValue: rawLocal };
|
|
62
|
+
}
|
|
63
|
+
const allowed = scale === 'fibonacci'
|
|
64
|
+
? FIBONACCI_SCALE
|
|
65
|
+
: scale === 'linear'
|
|
66
|
+
? LINEAR_SCALE
|
|
67
|
+
: scale === 'exponential'
|
|
68
|
+
? EXPONENTIAL_SCALE
|
|
69
|
+
: undefined;
|
|
70
|
+
if (!allowed) {
|
|
71
|
+
// Unknown scale value returned by Linear — skip rather than guess.
|
|
72
|
+
return { kind: 'skip-not-used' };
|
|
73
|
+
}
|
|
74
|
+
const snapped = snapToNearest(parsed, allowed);
|
|
75
|
+
return {
|
|
76
|
+
kind: 'mapped',
|
|
77
|
+
estimate: snapped,
|
|
78
|
+
originalValue: parsed,
|
|
79
|
+
snapped: snapped !== parsed,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=estimate-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"estimate-resolver.js","sourceRoot":"","sources":["../../../src/services/linear/estimate-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,eAAe,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAU,CAAC;AAC5D,MAAM,YAAY,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAU,CAAC;AACjD,MAAM,iBAAiB,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAU,CAAC;AAEvD,SAAS,aAAa,CAAC,KAAa,EAAE,KAAwB;IAC5D,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAC1C,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;QAC7C,oEAAoE;QACpE,mEAAmE;QACnE,IAAI,QAAQ,GAAG,YAAY,IAAI,CAAC,QAAQ,KAAK,YAAY,IAAI,SAAS,GAAG,IAAI,CAAC,EAAE,CAAC;YAC/E,IAAI,GAAG,SAAS,CAAC;YACjB,YAAY,GAAG,QAAQ,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAcD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,sBAAsB,CACpC,WAAoC,EACpC,KAAqD;IAErD,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC;QAClC,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;IACnC,CAAC;IACD,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,mEAAmE;QACnE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAClC,CAAC;IAED,MAAM,QAAQ,GACZ,WAAW,CAAC,eAAe,KAAK,SAAS;QACvC,CAAC,CAAC,WAAW,CAAC,eAAe;QAC7B,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC;IAC9B,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QACnE,OAAO,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC;IACzC,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1E,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAC5D,CAAC;IAED,MAAM,OAAO,GACX,KAAK,KAAK,WAAW;QACnB,CAAC,CAAC,eAAe;QACjB,CAAC,CAAC,KAAK,KAAK,QAAQ;YAClB,CAAC,CAAC,YAAY;YACd,CAAC,CAAC,KAAK,KAAK,aAAa;gBACvB,CAAC,CAAC,iBAAiB;gBACnB,CAAC,CAAC,SAAS,CAAC;IACpB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,mEAAmE;QACnE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;IACnC,CAAC;IAED,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/C,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,OAAO;QACjB,aAAa,EAAE,MAAM;QACrB,OAAO,EAAE,OAAO,KAAK,MAAM;KAC5B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure plan-building for `planr linear push --dry-run`. Takes loaded scopes
|
|
3
|
+
* and returns `LinearPushPlan` objects with per-kind row counts. No Linear
|
|
4
|
+
* API calls; no mutations. Epic-scope plans cascade rows for linked QT/BL.
|
|
5
|
+
*/
|
|
6
|
+
import type { Epic, Feature, OpenPlanrConfig, UserStory } from '../../models/types.js';
|
|
7
|
+
import { type ScopedFeature, type ScopedTaskFile } from './scope-loaders.js';
|
|
8
|
+
export type LinearPushItemKind = 'project' | 'feature' | 'story' | 'taskList' | 'quickTask' | 'backlogItem';
|
|
9
|
+
export type LinearPushAction = 'create' | 'update' | 'skip';
|
|
10
|
+
/** Scope of a granular push — what subtree `runLinearPush(artifactId)` touches. */
|
|
11
|
+
export type LinearPushScope = 'epic' | 'feature' | 'story' | 'taskFile' | 'quick' | 'backlog';
|
|
12
|
+
export interface LinearPushPlanRow {
|
|
13
|
+
kind: LinearPushItemKind;
|
|
14
|
+
/** Epic id, feature id, story id, or task-file id for this row. */
|
|
15
|
+
artifactId: string;
|
|
16
|
+
title: string;
|
|
17
|
+
action: LinearPushAction;
|
|
18
|
+
detail?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface LinearPushPlan {
|
|
21
|
+
/** The artifact the user pointed `planr linear push` at (any supported prefix). */
|
|
22
|
+
rootArtifactId: string;
|
|
23
|
+
/** The epic that owns this push's subtree; `undefined` for standalone QT/BL pushes. */
|
|
24
|
+
epicId?: string;
|
|
25
|
+
scope: LinearPushScope;
|
|
26
|
+
rows: LinearPushPlanRow[];
|
|
27
|
+
/** Counts by kind for non-`skip` rows. Missing kinds are 0. */
|
|
28
|
+
counts: {
|
|
29
|
+
project: number;
|
|
30
|
+
features: number;
|
|
31
|
+
stories: number;
|
|
32
|
+
taskLists: number;
|
|
33
|
+
quickTasks: number;
|
|
34
|
+
backlogItems: number;
|
|
35
|
+
total: number;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extract the linked epic id from QT / BL frontmatter. Canonical field is
|
|
40
|
+
* `epicId`; `parentEpic` is accepted as a compat alias for hand-authored
|
|
41
|
+
* files. Empty strings are normalised to `undefined`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function getLinkedEpicId(fm: Record<string, unknown>): string | undefined;
|
|
44
|
+
export declare function projectRow(epic: Epic): LinearPushPlanRow;
|
|
45
|
+
export declare function featureRow(f: Feature): LinearPushPlanRow;
|
|
46
|
+
export declare function storyRow(s: UserStory): LinearPushPlanRow;
|
|
47
|
+
export declare function taskListPlanRow(featureId: string, taskFiles: ScopedTaskFile[], hasBody: boolean, hadIssue: boolean): LinearPushPlanRow;
|
|
48
|
+
export declare function applyUpdateOnly(rows: LinearPushPlanRow[], updateOnly: boolean): LinearPushPlanRow[];
|
|
49
|
+
export declare function summarizePlan(rootArtifactId: string, epicId: string | undefined, scope: LinearPushScope, rows: LinearPushPlanRow[]): LinearPushPlan;
|
|
50
|
+
export declare function buildFeaturePlanRows(projectDir: string, config: OpenPlanrConfig, sf: ScopedFeature): Promise<LinearPushPlanRow[]>;
|
|
51
|
+
export declare function buildEpicPlanRows(projectDir: string, config: OpenPlanrConfig, epicScope: {
|
|
52
|
+
epic: Epic;
|
|
53
|
+
features: ScopedFeature[];
|
|
54
|
+
}): Promise<LinearPushPlanRow[]>;
|
|
55
|
+
/**
|
|
56
|
+
* Build a push preview (and counts) for `planr linear push --dry-run` at any
|
|
57
|
+
* granularity. Accepts any supported artifact id prefix (EPIC/FEAT/US/TASK/
|
|
58
|
+
* QT/BL); returns `null` when the artifact can't be resolved or is not
|
|
59
|
+
* pushable (ADR/SPRINT/checklist).
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildLinearPushPlan(projectDir: string, config: OpenPlanrConfig, artifactId: string, options?: {
|
|
62
|
+
updateOnly?: boolean;
|
|
63
|
+
}): Promise<LinearPushPlan | null>;
|
|
64
|
+
//# sourceMappingURL=plan-builders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-builders.d.ts","sourceRoot":"","sources":["../../../src/services/linear/plan-builders.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGvF,OAAO,EAOL,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,kBAAkB,GAC1B,SAAS,GACT,SAAS,GACT,OAAO,GACP,UAAU,GACV,WAAW,GACX,aAAa,CAAC;AAElB,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE5D,mFAAmF;AACnF,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAE9F,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,kBAAkB,CAAC;IACzB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,mFAAmF;IACnF,cAAc,EAAE,MAAM,CAAC;IACvB,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,eAAe,CAAC;IACvB,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAC1B,+DAA+D;IAC/D,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,CAE/E;AAUD,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,iBAAiB,CAQxD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAOxD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,SAAS,GAAG,iBAAiB,CAOxD;AAED,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,cAAc,EAAE,EAC3B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,OAAO,GAChB,iBAAiB,CAiBnB;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,iBAAiB,EAAE,EACzB,UAAU,EAAE,OAAO,GAClB,iBAAiB,EAAE,CAarB;AAED,wBAAgB,aAAa,CAC3B,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,iBAAiB,EAAE,GACxB,cAAc,CAwBhB;AAMD,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,eAAe,EACvB,EAAE,EAAE,aAAa,GAChB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAiB9B;AAED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,GACnD,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAiC9B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA2FhC"}
|