@proletariat/cli 0.3.44 → 0.3.46
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/dist/commands/agent/list.js +2 -3
- package/dist/commands/agent/login.js +2 -2
- package/dist/commands/agent/rebuild.js +2 -3
- package/dist/commands/agent/shell.js +2 -2
- package/dist/commands/agent/status.js +3 -3
- package/dist/commands/agent/visit.js +2 -2
- package/dist/commands/config/index.js +39 -1
- package/dist/commands/linear/auth.d.ts +14 -0
- package/dist/commands/linear/auth.js +211 -0
- package/dist/commands/linear/import.d.ts +21 -0
- package/dist/commands/linear/import.js +260 -0
- package/dist/commands/linear/status.d.ts +11 -0
- package/dist/commands/linear/status.js +88 -0
- package/dist/commands/linear/sync.d.ts +15 -0
- package/dist/commands/linear/sync.js +233 -0
- package/dist/commands/orchestrator/attach.d.ts +9 -1
- package/dist/commands/orchestrator/attach.js +67 -13
- package/dist/commands/orchestrator/index.js +22 -7
- package/dist/commands/staff/list.js +2 -3
- package/dist/commands/ticket/link/duplicates.d.ts +15 -0
- package/dist/commands/ticket/link/duplicates.js +95 -0
- package/dist/commands/ticket/link/index.js +14 -0
- package/dist/commands/ticket/link/relates.d.ts +15 -0
- package/dist/commands/ticket/link/relates.js +95 -0
- package/dist/commands/work/revise.js +7 -6
- package/dist/commands/work/spawn.d.ts +5 -0
- package/dist/commands/work/spawn.js +195 -14
- package/dist/commands/work/start.js +79 -23
- package/dist/commands/work/watch.js +2 -2
- package/dist/lib/agents/commands.d.ts +11 -0
- package/dist/lib/agents/commands.js +40 -10
- package/dist/lib/execution/config.d.ts +15 -0
- package/dist/lib/execution/config.js +54 -0
- package/dist/lib/execution/devcontainer.d.ts +6 -3
- package/dist/lib/execution/devcontainer.js +39 -12
- package/dist/lib/execution/runners.d.ts +28 -32
- package/dist/lib/execution/runners.js +345 -271
- package/dist/lib/execution/spawner.js +65 -7
- package/dist/lib/execution/types.d.ts +4 -0
- package/dist/lib/execution/types.js +3 -0
- package/dist/lib/external-issues/adapters.d.ts +26 -0
- package/dist/lib/external-issues/adapters.js +251 -0
- package/dist/lib/external-issues/index.d.ts +10 -0
- package/dist/lib/external-issues/index.js +14 -0
- package/dist/lib/external-issues/mapper.d.ts +21 -0
- package/dist/lib/external-issues/mapper.js +86 -0
- package/dist/lib/external-issues/types.d.ts +144 -0
- package/dist/lib/external-issues/types.js +26 -0
- package/dist/lib/external-issues/validation.d.ts +34 -0
- package/dist/lib/external-issues/validation.js +219 -0
- package/dist/lib/linear/client.d.ts +55 -0
- package/dist/lib/linear/client.js +254 -0
- package/dist/lib/linear/config.d.ts +37 -0
- package/dist/lib/linear/config.js +100 -0
- package/dist/lib/linear/index.d.ts +11 -0
- package/dist/lib/linear/index.js +10 -0
- package/dist/lib/linear/mapper.d.ts +67 -0
- package/dist/lib/linear/mapper.js +219 -0
- package/dist/lib/linear/sync.d.ts +37 -0
- package/dist/lib/linear/sync.js +89 -0
- package/dist/lib/linear/types.d.ts +139 -0
- package/dist/lib/linear/types.js +34 -0
- package/dist/lib/mcp/helpers.d.ts +8 -0
- package/dist/lib/mcp/helpers.js +10 -0
- package/dist/lib/mcp/tools/board.js +63 -11
- package/dist/lib/pmo/schema.d.ts +2 -0
- package/dist/lib/pmo/schema.js +20 -0
- package/dist/lib/pmo/storage/base.js +92 -13
- package/dist/lib/pmo/storage/dependencies.js +15 -0
- package/dist/lib/prompt-json.d.ts +4 -0
- package/dist/lib/themes.js +32 -16
- package/oclif.manifest.json +2823 -2336
- package/package.json +2 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External Issue Adapter Types
|
|
3
|
+
*
|
|
4
|
+
* Canonical types for normalizing issues from external sources
|
|
5
|
+
* (Linear and Jira) into a shared IssueEnvelope format
|
|
6
|
+
* that can be mapped to spawn context.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Supported external issue sources.
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
export type IssueSource = 'linear' | 'jira';
|
|
13
|
+
/**
|
|
14
|
+
* All valid issue sources as a const array.
|
|
15
|
+
*/
|
|
16
|
+
export declare const ISSUE_SOURCES: readonly ["linear", "jira"];
|
|
17
|
+
/**
|
|
18
|
+
* Canonical envelope for external issues/work items.
|
|
19
|
+
*
|
|
20
|
+
* Normalizes issues from different sources into a shared structure that can be
|
|
21
|
+
* deterministically mapped to spawn context.
|
|
22
|
+
*
|
|
23
|
+
* Source-specific fields are preserved in the `raw` payload.
|
|
24
|
+
*/
|
|
25
|
+
export interface IssueEnvelope {
|
|
26
|
+
/** Which external system this issue came from */
|
|
27
|
+
source: IssueSource;
|
|
28
|
+
/** Unique identifier in the external system (e.g., Linear UUID, Jira issue ID) */
|
|
29
|
+
external_id: string;
|
|
30
|
+
/** Human-readable key in the external system (e.g., "ENG-123", "PROJ-456") */
|
|
31
|
+
external_key: string;
|
|
32
|
+
/** Issue title / summary */
|
|
33
|
+
title: string;
|
|
34
|
+
/** Issue description (markdown or plain text) */
|
|
35
|
+
description: string;
|
|
36
|
+
/** Labels / tags applied to the issue */
|
|
37
|
+
labels: string[];
|
|
38
|
+
/** Priority level (normalized to P0-P3 scale) */
|
|
39
|
+
priority: string | null;
|
|
40
|
+
/** Current status name in the external system */
|
|
41
|
+
status: string;
|
|
42
|
+
/** URL to view the issue in the external system */
|
|
43
|
+
url: string;
|
|
44
|
+
/** Project key or identifier in the external system */
|
|
45
|
+
project_key: string;
|
|
46
|
+
/** Assignee display name or identifier */
|
|
47
|
+
assignee: string | null;
|
|
48
|
+
/**
|
|
49
|
+
* Source-native work item kind when available (e.g., issue, ticket, task).
|
|
50
|
+
* Optional to preserve compatibility with adapters that do not expose a
|
|
51
|
+
* stable item kind.
|
|
52
|
+
*/
|
|
53
|
+
item_type?: string | null;
|
|
54
|
+
/** Original source-specific payload (preserved for source-specific logic) */
|
|
55
|
+
raw: Record<string, unknown>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Metadata derived from an IssueEnvelope for spawn context.
|
|
59
|
+
* Used to populate ExecutionContext fields when spawning agent work
|
|
60
|
+
* from an external issue.
|
|
61
|
+
*/
|
|
62
|
+
export interface IssueSpawnContext {
|
|
63
|
+
/** Prompt text generated from the issue for the agent */
|
|
64
|
+
prompt: string;
|
|
65
|
+
/** Metadata key-value pairs to attach to the ticket */
|
|
66
|
+
metadata: Record<string, string>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Error codes for issue envelope validation failures.
|
|
70
|
+
*/
|
|
71
|
+
export type IssueValidationErrorCode = 'MISSING_FIELD' | 'INVALID_SOURCE' | 'INVALID_FIELD_TYPE' | 'EMPTY_FIELD';
|
|
72
|
+
/**
|
|
73
|
+
* Structured validation error for issue envelope fields.
|
|
74
|
+
*/
|
|
75
|
+
export interface IssueValidationError {
|
|
76
|
+
/** Machine-readable error code */
|
|
77
|
+
code: IssueValidationErrorCode;
|
|
78
|
+
/** The field that failed validation */
|
|
79
|
+
field: string;
|
|
80
|
+
/** Human-readable error message */
|
|
81
|
+
message: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Result of validating an issue envelope.
|
|
85
|
+
*/
|
|
86
|
+
export type IssueValidationResult = {
|
|
87
|
+
valid: true;
|
|
88
|
+
envelope: IssueEnvelope;
|
|
89
|
+
} | {
|
|
90
|
+
valid: false;
|
|
91
|
+
errors: IssueValidationError[];
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Contract for external issue source adapters.
|
|
95
|
+
*
|
|
96
|
+
* Both Linear and Jira adapters must implement this interface to normalize
|
|
97
|
+
* their issues into the shared IssueEnvelope format.
|
|
98
|
+
*
|
|
99
|
+
* Adapters are responsible for:
|
|
100
|
+
* 1. Fetching issues from their source API
|
|
101
|
+
* 2. Normalizing source-specific data into IssueEnvelope format
|
|
102
|
+
* 3. Preserving source-specific fields in the `raw` payload
|
|
103
|
+
*/
|
|
104
|
+
export interface ExternalIssueAdapter {
|
|
105
|
+
/** Which source this adapter handles */
|
|
106
|
+
readonly source: IssueSource;
|
|
107
|
+
/**
|
|
108
|
+
* Normalize a raw API response into an IssueEnvelope.
|
|
109
|
+
*
|
|
110
|
+
* @param raw - Raw issue data from the source API
|
|
111
|
+
* @returns Validated IssueEnvelope
|
|
112
|
+
* @throws ExternalIssueError if the raw data cannot be normalized
|
|
113
|
+
*/
|
|
114
|
+
normalize(raw: unknown): IssueEnvelope;
|
|
115
|
+
/**
|
|
116
|
+
* Fetch and normalize a single issue by its external key.
|
|
117
|
+
*
|
|
118
|
+
* @param key - External issue key (e.g., "ENG-123" for Linear, "PROJ-456" for Jira)
|
|
119
|
+
* @returns Normalized IssueEnvelope
|
|
120
|
+
* @throws ExternalIssueError if the issue cannot be fetched or normalized
|
|
121
|
+
*/
|
|
122
|
+
fetchByKey(key: string): Promise<IssueEnvelope>;
|
|
123
|
+
/**
|
|
124
|
+
* Fetch and normalize multiple issues matching a query.
|
|
125
|
+
*
|
|
126
|
+
* @param query - Source-specific query parameters
|
|
127
|
+
* @returns Array of normalized IssueEnvelopes
|
|
128
|
+
* @throws ExternalIssueError if the query fails
|
|
129
|
+
*/
|
|
130
|
+
fetchByQuery(query: Record<string, unknown>): Promise<IssueEnvelope[]>;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Error codes for external issue operations.
|
|
134
|
+
*/
|
|
135
|
+
export type ExternalIssueErrorCode = 'VALIDATION_FAILED' | 'FETCH_FAILED' | 'NORMALIZE_FAILED' | 'SOURCE_NOT_SUPPORTED';
|
|
136
|
+
/**
|
|
137
|
+
* Typed error for external issue operations.
|
|
138
|
+
*/
|
|
139
|
+
export declare class ExternalIssueError extends Error {
|
|
140
|
+
code: ExternalIssueErrorCode;
|
|
141
|
+
source?: IssueSource | undefined;
|
|
142
|
+
validationErrors?: IssueValidationError[] | undefined;
|
|
143
|
+
constructor(code: ExternalIssueErrorCode, message: string, source?: IssueSource | undefined, validationErrors?: IssueValidationError[] | undefined);
|
|
144
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External Issue Adapter Types
|
|
3
|
+
*
|
|
4
|
+
* Canonical types for normalizing issues from external sources
|
|
5
|
+
* (Linear and Jira) into a shared IssueEnvelope format
|
|
6
|
+
* that can be mapped to spawn context.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* All valid issue sources as a const array.
|
|
10
|
+
*/
|
|
11
|
+
export const ISSUE_SOURCES = ['linear', 'jira'];
|
|
12
|
+
/**
|
|
13
|
+
* Typed error for external issue operations.
|
|
14
|
+
*/
|
|
15
|
+
export class ExternalIssueError extends Error {
|
|
16
|
+
code;
|
|
17
|
+
source;
|
|
18
|
+
validationErrors;
|
|
19
|
+
constructor(code, message, source, validationErrors) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.source = source;
|
|
23
|
+
this.validationErrors = validationErrors;
|
|
24
|
+
this.name = 'ExternalIssueError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IssueEnvelope Validation
|
|
3
|
+
*
|
|
4
|
+
* Validates raw data into a canonical IssueEnvelope.
|
|
5
|
+
* Provides typed validation errors for missing or invalid fields.
|
|
6
|
+
*/
|
|
7
|
+
import { type IssueEnvelope, type IssueValidationResult } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Validate and construct an IssueEnvelope from untyped input.
|
|
10
|
+
*
|
|
11
|
+
* Checks:
|
|
12
|
+
* - `source` is a valid IssueSource
|
|
13
|
+
* - Required string fields are present and non-empty
|
|
14
|
+
* - `description` is present (may be empty string)
|
|
15
|
+
* - `labels` is an array of strings
|
|
16
|
+
* - `priority` is a string or null
|
|
17
|
+
* - `assignee` is a string or null
|
|
18
|
+
* - `item_type` is optional, but if present must be a non-empty string or null
|
|
19
|
+
* - `raw` is a plain object
|
|
20
|
+
*
|
|
21
|
+
* @param input - Untyped input to validate
|
|
22
|
+
* @returns Validation result with either the valid envelope or an array of errors
|
|
23
|
+
*/
|
|
24
|
+
export declare function validateIssueEnvelope(input: unknown): IssueValidationResult;
|
|
25
|
+
/**
|
|
26
|
+
* Validate an IssueEnvelope or throw an ExternalIssueError.
|
|
27
|
+
*
|
|
28
|
+
* Convenience wrapper around validateIssueEnvelope that throws on failure.
|
|
29
|
+
*
|
|
30
|
+
* @param input - Untyped input to validate
|
|
31
|
+
* @returns Valid IssueEnvelope
|
|
32
|
+
* @throws ExternalIssueError with code 'VALIDATION_FAILED' if invalid
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateOrThrow(input: unknown): IssueEnvelope;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IssueEnvelope Validation
|
|
3
|
+
*
|
|
4
|
+
* Validates raw data into a canonical IssueEnvelope.
|
|
5
|
+
* Provides typed validation errors for missing or invalid fields.
|
|
6
|
+
*/
|
|
7
|
+
import { ISSUE_SOURCES, ExternalIssueError, } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Required string fields on IssueEnvelope (must be present and non-empty).
|
|
10
|
+
*/
|
|
11
|
+
const REQUIRED_STRING_FIELDS = [
|
|
12
|
+
'external_id',
|
|
13
|
+
'external_key',
|
|
14
|
+
'title',
|
|
15
|
+
'status',
|
|
16
|
+
'url',
|
|
17
|
+
'project_key',
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Validate and construct an IssueEnvelope from untyped input.
|
|
21
|
+
*
|
|
22
|
+
* Checks:
|
|
23
|
+
* - `source` is a valid IssueSource
|
|
24
|
+
* - Required string fields are present and non-empty
|
|
25
|
+
* - `description` is present (may be empty string)
|
|
26
|
+
* - `labels` is an array of strings
|
|
27
|
+
* - `priority` is a string or null
|
|
28
|
+
* - `assignee` is a string or null
|
|
29
|
+
* - `item_type` is optional, but if present must be a non-empty string or null
|
|
30
|
+
* - `raw` is a plain object
|
|
31
|
+
*
|
|
32
|
+
* @param input - Untyped input to validate
|
|
33
|
+
* @returns Validation result with either the valid envelope or an array of errors
|
|
34
|
+
*/
|
|
35
|
+
export function validateIssueEnvelope(input) {
|
|
36
|
+
const errors = [];
|
|
37
|
+
if (typeof input !== 'object' || input === null) {
|
|
38
|
+
errors.push({
|
|
39
|
+
code: 'INVALID_FIELD_TYPE',
|
|
40
|
+
field: '(root)',
|
|
41
|
+
message: 'Input must be a non-null object',
|
|
42
|
+
});
|
|
43
|
+
return { valid: false, errors };
|
|
44
|
+
}
|
|
45
|
+
const data = input;
|
|
46
|
+
// Validate source
|
|
47
|
+
if (!('source' in data) || data.source === undefined || data.source === null) {
|
|
48
|
+
errors.push({
|
|
49
|
+
code: 'MISSING_FIELD',
|
|
50
|
+
field: 'source',
|
|
51
|
+
message: 'source is required',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
else if (typeof data.source !== 'string') {
|
|
55
|
+
errors.push({
|
|
56
|
+
code: 'INVALID_FIELD_TYPE',
|
|
57
|
+
field: 'source',
|
|
58
|
+
message: 'source must be a string',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else if (!ISSUE_SOURCES.includes(data.source)) {
|
|
62
|
+
errors.push({
|
|
63
|
+
code: 'INVALID_SOURCE',
|
|
64
|
+
field: 'source',
|
|
65
|
+
message: `source must be one of: ${ISSUE_SOURCES.join(', ')}. Got: "${data.source}"`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// Validate required string fields
|
|
69
|
+
for (const field of REQUIRED_STRING_FIELDS) {
|
|
70
|
+
if (!(field in data) || data[field] === undefined || data[field] === null) {
|
|
71
|
+
errors.push({
|
|
72
|
+
code: 'MISSING_FIELD',
|
|
73
|
+
field,
|
|
74
|
+
message: `${field} is required`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
else if (typeof data[field] !== 'string') {
|
|
78
|
+
errors.push({
|
|
79
|
+
code: 'INVALID_FIELD_TYPE',
|
|
80
|
+
field,
|
|
81
|
+
message: `${field} must be a string`,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else if (data[field].trim() === '') {
|
|
85
|
+
errors.push({
|
|
86
|
+
code: 'EMPTY_FIELD',
|
|
87
|
+
field,
|
|
88
|
+
message: `${field} must not be empty`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Validate description (required, but may be empty string)
|
|
93
|
+
if (!('description' in data) || data.description === undefined || data.description === null) {
|
|
94
|
+
errors.push({
|
|
95
|
+
code: 'MISSING_FIELD',
|
|
96
|
+
field: 'description',
|
|
97
|
+
message: 'description is required',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else if (typeof data.description !== 'string') {
|
|
101
|
+
errors.push({
|
|
102
|
+
code: 'INVALID_FIELD_TYPE',
|
|
103
|
+
field: 'description',
|
|
104
|
+
message: 'description must be a string',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// Validate labels
|
|
108
|
+
if (!('labels' in data) || data.labels === undefined || data.labels === null) {
|
|
109
|
+
errors.push({
|
|
110
|
+
code: 'MISSING_FIELD',
|
|
111
|
+
field: 'labels',
|
|
112
|
+
message: 'labels is required',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else if (!Array.isArray(data.labels)) {
|
|
116
|
+
errors.push({
|
|
117
|
+
code: 'INVALID_FIELD_TYPE',
|
|
118
|
+
field: 'labels',
|
|
119
|
+
message: 'labels must be an array',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
for (let i = 0; i < data.labels.length; i++) {
|
|
124
|
+
if (typeof data.labels[i] !== 'string') {
|
|
125
|
+
errors.push({
|
|
126
|
+
code: 'INVALID_FIELD_TYPE',
|
|
127
|
+
field: `labels[${i}]`,
|
|
128
|
+
message: `labels[${i}] must be a string`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Validate priority (string or null)
|
|
134
|
+
if (!('priority' in data) || data.priority === undefined) {
|
|
135
|
+
errors.push({
|
|
136
|
+
code: 'MISSING_FIELD',
|
|
137
|
+
field: 'priority',
|
|
138
|
+
message: 'priority is required (use null if not set)',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
else if (data.priority !== null && typeof data.priority !== 'string') {
|
|
142
|
+
errors.push({
|
|
143
|
+
code: 'INVALID_FIELD_TYPE',
|
|
144
|
+
field: 'priority',
|
|
145
|
+
message: 'priority must be a string or null',
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
// Validate assignee (string or null)
|
|
149
|
+
if (!('assignee' in data) || data.assignee === undefined) {
|
|
150
|
+
errors.push({
|
|
151
|
+
code: 'MISSING_FIELD',
|
|
152
|
+
field: 'assignee',
|
|
153
|
+
message: 'assignee is required (use null if not assigned)',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else if (data.assignee !== null && typeof data.assignee !== 'string') {
|
|
157
|
+
errors.push({
|
|
158
|
+
code: 'INVALID_FIELD_TYPE',
|
|
159
|
+
field: 'assignee',
|
|
160
|
+
message: 'assignee must be a string or null',
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
// Validate item_type (optional: string or null)
|
|
164
|
+
if ('item_type' in data && data.item_type !== undefined) {
|
|
165
|
+
if (data.item_type !== null && typeof data.item_type !== 'string') {
|
|
166
|
+
errors.push({
|
|
167
|
+
code: 'INVALID_FIELD_TYPE',
|
|
168
|
+
field: 'item_type',
|
|
169
|
+
message: 'item_type must be a string or null',
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
else if (typeof data.item_type === 'string' && data.item_type.trim() === '') {
|
|
173
|
+
errors.push({
|
|
174
|
+
code: 'EMPTY_FIELD',
|
|
175
|
+
field: 'item_type',
|
|
176
|
+
message: 'item_type must not be empty when provided',
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Validate raw
|
|
181
|
+
if (!('raw' in data) || data.raw === undefined || data.raw === null) {
|
|
182
|
+
errors.push({
|
|
183
|
+
code: 'MISSING_FIELD',
|
|
184
|
+
field: 'raw',
|
|
185
|
+
message: 'raw is required',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
else if (typeof data.raw !== 'object' || Array.isArray(data.raw)) {
|
|
189
|
+
errors.push({
|
|
190
|
+
code: 'INVALID_FIELD_TYPE',
|
|
191
|
+
field: 'raw',
|
|
192
|
+
message: 'raw must be a plain object',
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (errors.length > 0) {
|
|
196
|
+
return { valid: false, errors };
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
valid: true,
|
|
200
|
+
envelope: data,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Validate an IssueEnvelope or throw an ExternalIssueError.
|
|
205
|
+
*
|
|
206
|
+
* Convenience wrapper around validateIssueEnvelope that throws on failure.
|
|
207
|
+
*
|
|
208
|
+
* @param input - Untyped input to validate
|
|
209
|
+
* @returns Valid IssueEnvelope
|
|
210
|
+
* @throws ExternalIssueError with code 'VALIDATION_FAILED' if invalid
|
|
211
|
+
*/
|
|
212
|
+
export function validateOrThrow(input) {
|
|
213
|
+
const result = validateIssueEnvelope(input);
|
|
214
|
+
if (!result.valid) {
|
|
215
|
+
const fieldList = result.errors.map((e) => e.field).join(', ');
|
|
216
|
+
throw new ExternalIssueError('VALIDATION_FAILED', `Issue envelope validation failed: invalid fields [${fieldList}]`, undefined, result.errors);
|
|
217
|
+
}
|
|
218
|
+
return result.envelope;
|
|
219
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linear API Client
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper around @linear/sdk providing typed access to
|
|
5
|
+
* Linear issues, teams, states, and cycles for the PMO integration.
|
|
6
|
+
*/
|
|
7
|
+
import type { LinearIssue, LinearTeam, LinearWorkflowState, LinearCycle, LinearIssueFilter } from './types.js';
|
|
8
|
+
export declare class LinearClient {
|
|
9
|
+
private sdk;
|
|
10
|
+
constructor(apiKey: string);
|
|
11
|
+
/**
|
|
12
|
+
* Verify the API key is valid and return the authenticated user's organization.
|
|
13
|
+
*/
|
|
14
|
+
verify(): Promise<{
|
|
15
|
+
organizationName: string;
|
|
16
|
+
userName: string;
|
|
17
|
+
email: string;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* List all teams in the workspace.
|
|
21
|
+
*/
|
|
22
|
+
listTeams(): Promise<LinearTeam[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Get a team by its key (e.g., "ENG").
|
|
25
|
+
*/
|
|
26
|
+
getTeamByKey(key: string): Promise<LinearTeam | null>;
|
|
27
|
+
/**
|
|
28
|
+
* List workflow states for a team.
|
|
29
|
+
*/
|
|
30
|
+
listStates(teamId: string): Promise<LinearWorkflowState[]>;
|
|
31
|
+
/**
|
|
32
|
+
* List cycles for a team.
|
|
33
|
+
*/
|
|
34
|
+
listCycles(teamId: string): Promise<LinearCycle[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Fetch issues from Linear with optional filters.
|
|
37
|
+
*/
|
|
38
|
+
listIssues(filter?: LinearIssueFilter): Promise<LinearIssue[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Fetch a single issue by its identifier (e.g., "ENG-123").
|
|
41
|
+
*/
|
|
42
|
+
getIssueByIdentifier(identifier: string): Promise<LinearIssue | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Update the state of an issue.
|
|
45
|
+
*/
|
|
46
|
+
updateIssueState(issueId: string, stateId: string): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Add a comment to an issue.
|
|
49
|
+
*/
|
|
50
|
+
addComment(issueId: string, body: string): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Attach a URL to an issue (e.g., a PR link).
|
|
53
|
+
*/
|
|
54
|
+
attachUrl(issueId: string, url: string, title: string): Promise<void>;
|
|
55
|
+
}
|