@fink-andreas/pi-linear-tools 0.4.2 → 0.4.3
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/CHANGELOG.md +16 -0
- package/README.md +131 -3
- package/extensions/pi-linear-tools.js +180 -10
- package/index.js +1 -1019
- package/package.json +2 -2
- package/src/cli.js +700 -6
- package/src/handlers.js +403 -0
- package/src/linear.js +1218 -9
- package/src/sync-doc.js +1208 -0
package/src/cli.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { loadSettings, saveSettings } from './settings.js';
|
|
2
2
|
import { createLinearClient } from './linear-client.js';
|
|
3
3
|
import { resolveProjectRef } from './linear.js';
|
|
4
|
+
import {
|
|
5
|
+
explainSyncDocSetup,
|
|
6
|
+
initSyncDocConfig,
|
|
7
|
+
listSyncDocTargets,
|
|
8
|
+
runAllSyncDocs,
|
|
9
|
+
runSyncDoc,
|
|
10
|
+
} from './sync-doc.js';
|
|
4
11
|
import {
|
|
5
12
|
authenticate,
|
|
6
13
|
logout,
|
|
@@ -11,12 +18,25 @@ import {
|
|
|
11
18
|
import {
|
|
12
19
|
executeIssueList,
|
|
13
20
|
executeIssueView,
|
|
21
|
+
executeIssueActivity,
|
|
14
22
|
executeIssueCreate,
|
|
15
23
|
executeIssueUpdate,
|
|
16
24
|
executeIssueComment,
|
|
17
25
|
executeIssueStart,
|
|
18
26
|
executeIssueDelete,
|
|
19
27
|
executeProjectList,
|
|
28
|
+
executeProjectView,
|
|
29
|
+
executeProjectCreate,
|
|
30
|
+
executeProjectUpdate,
|
|
31
|
+
executeProjectDelete,
|
|
32
|
+
executeProjectArchive,
|
|
33
|
+
executeProjectUnarchive,
|
|
34
|
+
executeProjectUpdateList,
|
|
35
|
+
executeProjectUpdateView,
|
|
36
|
+
executeProjectUpdateCreate,
|
|
37
|
+
executeProjectUpdateUpdate,
|
|
38
|
+
executeProjectUpdateArchive,
|
|
39
|
+
executeProjectUpdateUnarchive,
|
|
20
40
|
executeTeamList,
|
|
21
41
|
executeMilestoneList,
|
|
22
42
|
executeMilestoneView,
|
|
@@ -131,8 +151,10 @@ Usage:
|
|
|
131
151
|
pi-linear-tools <command> [options]
|
|
132
152
|
|
|
133
153
|
Commands:
|
|
134
|
-
issue <action> [options] Manage issues
|
|
135
|
-
project <action> [options] Manage projects
|
|
154
|
+
issue <action> [options] Manage issues, comments, and issue activity/history
|
|
155
|
+
project <action> [options] Manage projects and project metadata
|
|
156
|
+
project-update <action> [options] Manage project updates (Linear Updates tab entries)
|
|
157
|
+
sync-doc [action] [options] Sync local markdown into Linear fields
|
|
136
158
|
team <action> [options] Manage teams
|
|
137
159
|
milestone <action> [options] Manage milestones
|
|
138
160
|
|
|
@@ -151,6 +173,7 @@ Auth Actions:
|
|
|
151
173
|
Issue Actions:
|
|
152
174
|
list [--project X] [--states X,Y] [--assignee me|all] [--team X] [--limit N]
|
|
153
175
|
view <issue> [--no-comments]
|
|
176
|
+
activity <issue> [--limit N] [--include-archived true|false]
|
|
154
177
|
create --title X [--team X] [--project X] [--description X] [--priority 0-4] [--assignee me|ID]
|
|
155
178
|
update <issue> [--title X] [--description X] [--state X] [--priority 0-4]
|
|
156
179
|
[--assignee me|ID] [--milestone X] [--sub-issue-of X]
|
|
@@ -160,6 +183,20 @@ Issue Actions:
|
|
|
160
183
|
|
|
161
184
|
Project Actions:
|
|
162
185
|
list
|
|
186
|
+
view <project>
|
|
187
|
+
create --name X --teams ENG,OPS [--description X] [--lead me|ID] [--priority 0-4] [--target-date YYYY-MM-DD]
|
|
188
|
+
update <project> [--name X] [--description X] [--teams X,Y] [--lead me|none|ID] [--target-date YYYY-MM-DD]
|
|
189
|
+
delete <project>
|
|
190
|
+
archive <project>
|
|
191
|
+
unarchive <project>
|
|
192
|
+
|
|
193
|
+
Project Update Actions:
|
|
194
|
+
list --project X [--limit N] [--include-archived true|false]
|
|
195
|
+
view <project-update-id>
|
|
196
|
+
create --project X [--body X] [--health onTrack|atRisk|offTrack]
|
|
197
|
+
update <project-update-id> [--body X] [--health onTrack|atRisk|offTrack]
|
|
198
|
+
archive <project-update-id>
|
|
199
|
+
unarchive <project-update-id>
|
|
163
200
|
|
|
164
201
|
Team Actions:
|
|
165
202
|
list
|
|
@@ -171,6 +208,36 @@ Milestone Actions:
|
|
|
171
208
|
update <milestone-id> [--name X] [--description X] [--target-date X] [--status X]
|
|
172
209
|
delete <milestone-id>
|
|
173
210
|
|
|
211
|
+
Sync Doc Actions:
|
|
212
|
+
init [--cwd X] [--project X] [--file X]
|
|
213
|
+
explain
|
|
214
|
+
list [--config X]
|
|
215
|
+
run [--target X] [--config X]
|
|
216
|
+
check [--target X] [--config X]
|
|
217
|
+
run --file X --project X [--field content|description] [--marker X]
|
|
218
|
+
run --file X --issue X [--field description] [--marker X]
|
|
219
|
+
|
|
220
|
+
Command Notes:
|
|
221
|
+
issue update changes issue fields; issue activity reads the Activity timeline.
|
|
222
|
+
project update changes project fields; project-update manages Updates tab entries.
|
|
223
|
+
project-update maps to Linear project updates in the Updates tab.
|
|
224
|
+
sync-doc init scaffolds .linear-tools/config.json in the target folder.
|
|
225
|
+
sync-doc run/check defaults to all configured targets in .linear-tools/config.json.
|
|
226
|
+
sync-doc --target X narrows the operation to one configured target.
|
|
227
|
+
|
|
228
|
+
More Help:
|
|
229
|
+
pi-linear-tools issue --help
|
|
230
|
+
pi-linear-tools project --help
|
|
231
|
+
pi-linear-tools project-update --help
|
|
232
|
+
pi-linear-tools sync-doc --help
|
|
233
|
+
pi-linear-tools milestone --help
|
|
234
|
+
|
|
235
|
+
Reference Conventions:
|
|
236
|
+
issues issue key (ENG-123) or issue ID
|
|
237
|
+
projects project name or project ID
|
|
238
|
+
project-updates project update ID
|
|
239
|
+
milestones milestone ID
|
|
240
|
+
|
|
174
241
|
Common Flags:
|
|
175
242
|
--project Project name or ID
|
|
176
243
|
--team Team key (e.g., ENG)
|
|
@@ -182,10 +249,21 @@ Common Flags:
|
|
|
182
249
|
Examples:
|
|
183
250
|
pi-linear-tools auth login
|
|
184
251
|
pi-linear-tools auth status
|
|
252
|
+
pi-linear-tools issue update ENG-123 --state "In Progress" --assignee me
|
|
253
|
+
pi-linear-tools issue comment ENG-123 --body "Blocked on API review"
|
|
254
|
+
pi-linear-tools issue activity ENG-123 --limit 20
|
|
255
|
+
pi-linear-tools project view "Roadmap Refresh"
|
|
256
|
+
pi-linear-tools project update "Roadmap Refresh" --target-date 2026-04-15 --lead me
|
|
257
|
+
pi-linear-tools project-update create --project "Roadmap Refresh" --body "Weekly update" --health onTrack
|
|
258
|
+
pi-linear-tools project-update update 12345678-1234-1234-1234-123456789abc --health atRisk
|
|
259
|
+
pi-linear-tools sync-doc list
|
|
260
|
+
pi-linear-tools sync-doc init --cwd /path/to/subproject --project "Project name or ID"
|
|
261
|
+
pi-linear-tools sync-doc explain
|
|
262
|
+
pi-linear-tools sync-doc run
|
|
263
|
+
pi-linear-tools sync-doc check
|
|
185
264
|
pi-linear-tools issue list --project MyProject --states "In Progress,Backlog"
|
|
186
265
|
pi-linear-tools issue view ENG-123
|
|
187
266
|
pi-linear-tools issue create --title "Fix bug" --team ENG --priority 2
|
|
188
|
-
pi-linear-tools issue update ENG-123 --state "In Progress" --assignee me
|
|
189
267
|
pi-linear-tools issue start ENG-123
|
|
190
268
|
pi-linear-tools milestone list --project MyProject
|
|
191
269
|
pi-linear-tools config --api-key lin_xxx
|
|
@@ -204,9 +282,15 @@ function printIssueHelp() {
|
|
|
204
282
|
Usage:
|
|
205
283
|
pi-linear-tools issue <action> [options]
|
|
206
284
|
|
|
285
|
+
Guidance:
|
|
286
|
+
Use "update" to change the issue itself: title, description, state, assignee, milestone, or parent.
|
|
287
|
+
Use "comment" to add a discussion entry.
|
|
288
|
+
Use "activity" to read the Activity timeline shown in Linear.
|
|
289
|
+
|
|
207
290
|
Actions:
|
|
208
291
|
list List issues in a project
|
|
209
292
|
view View issue details
|
|
293
|
+
activity View issue activity/history
|
|
210
294
|
create Create a new issue
|
|
211
295
|
update Update an existing issue
|
|
212
296
|
comment Add a comment to an issue
|
|
@@ -221,9 +305,14 @@ List Options:
|
|
|
221
305
|
--limit N Max results (default: 50)
|
|
222
306
|
|
|
223
307
|
View Options:
|
|
224
|
-
<issue> Issue key (e.g., ENG-123) or
|
|
308
|
+
<issue> Issue key (e.g., ENG-123), ID, or issue URL
|
|
225
309
|
--no-comments Exclude comments from output
|
|
226
310
|
|
|
311
|
+
Activity Options:
|
|
312
|
+
<issue> Issue key, ID, or issue URL
|
|
313
|
+
--limit N Max activity entries to fetch (default: 20)
|
|
314
|
+
--include-archived X true or false
|
|
315
|
+
|
|
227
316
|
Create Options:
|
|
228
317
|
--title X Issue title (required)
|
|
229
318
|
--team X Team key, e.g., ENG (required if no default team)
|
|
@@ -254,6 +343,14 @@ Start Options:
|
|
|
254
343
|
|
|
255
344
|
Delete Options:
|
|
256
345
|
<issue> Issue key or ID
|
|
346
|
+
|
|
347
|
+
Examples:
|
|
348
|
+
pi-linear-tools issue view ENG-123
|
|
349
|
+
pi-linear-tools issue update ENG-123 --state "In Progress" --assignee me
|
|
350
|
+
pi-linear-tools issue update ENG-123 --milestone "Sprint 12" --priority 2
|
|
351
|
+
pi-linear-tools issue comment ENG-123 --body "Ready for review"
|
|
352
|
+
pi-linear-tools issue activity ENG-123 --limit 20
|
|
353
|
+
pi-linear-tools issue activity https://linear.app/workspace/issue/ENG-123/example --limit 20
|
|
257
354
|
`);
|
|
258
355
|
}
|
|
259
356
|
|
|
@@ -261,10 +358,175 @@ function printProjectHelp() {
|
|
|
261
358
|
console.log(`pi-linear-tools project - Manage Linear projects
|
|
262
359
|
|
|
263
360
|
Usage:
|
|
264
|
-
pi-linear-tools project <action>
|
|
361
|
+
pi-linear-tools project <action> [options]
|
|
362
|
+
|
|
363
|
+
Guidance:
|
|
364
|
+
Use "project update" to change the project record itself: name, teams, dates, lead, description, priority.
|
|
365
|
+
Use "project-update" for the Updates tab entries that appear as weekly or status updates in Linear.
|
|
366
|
+
|
|
367
|
+
Actions:
|
|
368
|
+
list List all accessible projects
|
|
369
|
+
view View project details
|
|
370
|
+
create Create a new project
|
|
371
|
+
update Update an existing project
|
|
372
|
+
delete Delete a project
|
|
373
|
+
archive Archive a project
|
|
374
|
+
unarchive Restore an archived project
|
|
375
|
+
|
|
376
|
+
View Options:
|
|
377
|
+
<project> Project name or ID
|
|
378
|
+
|
|
379
|
+
Create Options:
|
|
380
|
+
--name X Project name (required)
|
|
381
|
+
--teams X,Y Team keys or IDs (required)
|
|
382
|
+
--description X Project description
|
|
383
|
+
--lead X "me" or user ID
|
|
384
|
+
--priority N Priority 0-4
|
|
385
|
+
--color X Project color (hex)
|
|
386
|
+
--icon X Project icon
|
|
387
|
+
--start-date X Planned start date (YYYY-MM-DD)
|
|
388
|
+
--target-date X Planned target date (YYYY-MM-DD)
|
|
389
|
+
|
|
390
|
+
Update Options:
|
|
391
|
+
<project> Project name or ID
|
|
392
|
+
--name X New name
|
|
393
|
+
--teams X,Y Replace associated teams
|
|
394
|
+
--description X New description
|
|
395
|
+
--lead X "me", "none", or user ID
|
|
396
|
+
--priority N New priority 0-4
|
|
397
|
+
--color X New project color (hex)
|
|
398
|
+
--icon X New project icon
|
|
399
|
+
--start-date X New planned start date
|
|
400
|
+
--target-date X New planned target date
|
|
401
|
+
|
|
402
|
+
Delete Options:
|
|
403
|
+
<project> Project name or ID
|
|
404
|
+
|
|
405
|
+
Archive Options:
|
|
406
|
+
<project> Project name or ID
|
|
407
|
+
|
|
408
|
+
Unarchive Options:
|
|
409
|
+
<project> Project name or ID
|
|
410
|
+
|
|
411
|
+
Examples:
|
|
412
|
+
pi-linear-tools project view "Roadmap Refresh"
|
|
413
|
+
pi-linear-tools project update "Roadmap Refresh" --description "New scope" --target-date 2026-04-15
|
|
414
|
+
pi-linear-tools project update "Roadmap Refresh" --lead me --teams ENG,OPS
|
|
415
|
+
pi-linear-tools project archive "Roadmap Refresh"
|
|
416
|
+
`);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function printSyncDocHelp() {
|
|
420
|
+
console.log(`pi-linear-tools sync-doc - Sync markdown files into Linear
|
|
421
|
+
|
|
422
|
+
Usage:
|
|
423
|
+
pi-linear-tools sync-doc [init|explain|list|run|check] [options]
|
|
424
|
+
|
|
425
|
+
Config:
|
|
426
|
+
Reads targets from .linear-tools/config.json in the current project tree and ~/.linear-tools/config.json.
|
|
427
|
+
Put config in the smallest folder that owns the docs being synced. The nearest config wins.
|
|
428
|
+
Use repo root only for targets intentionally shared across multiple subprojects. Use ~/.linear-tools/config.json for personal defaults.
|
|
429
|
+
|
|
430
|
+
Actions:
|
|
431
|
+
init Scaffold a starter .linear-tools/config.json in the target folder
|
|
432
|
+
explain Print the recommended config placement and overview/document model
|
|
433
|
+
list Show resolved sync targets from config
|
|
434
|
+
run Update Linear if the managed block differs. Defaults to all configured targets.
|
|
435
|
+
check Show whether a sync would change Linear. Defaults to all configured targets.
|
|
436
|
+
|
|
437
|
+
Target Options:
|
|
438
|
+
--target X Target name from config
|
|
439
|
+
--config X Explicit path to config.json or .linear-tools/
|
|
440
|
+
--cwd X Resolve config and files relative to X
|
|
441
|
+
|
|
442
|
+
Init Options:
|
|
443
|
+
--cwd X Create .linear-tools/config.json inside X (defaults to current directory)
|
|
444
|
+
--project X Fill the starter overview target with a project name or ID
|
|
445
|
+
--file X File path for the starter overview target (default: README.md)
|
|
446
|
+
--field X content or description (default: content)
|
|
447
|
+
--force Overwrite an existing config.json
|
|
448
|
+
|
|
449
|
+
One-off Run/Check Options:
|
|
450
|
+
--file X Markdown file to sync
|
|
451
|
+
--project X Linear project name, id, slug, or project URL
|
|
452
|
+
--issue X Linear issue key or id
|
|
453
|
+
--target-type X projectField, issueField, or document
|
|
454
|
+
--document-title X Required for one-off document targets unless inferred from file name
|
|
455
|
+
--document-id X Existing Linear document ID or URL identifier
|
|
456
|
+
--field X For projects: content or description. For issues: description
|
|
457
|
+
--marker X Managed block marker (default: file basename without extension)
|
|
458
|
+
|
|
459
|
+
Managed Block:
|
|
460
|
+
<!-- linear-tools:sync-start MARKER -->
|
|
461
|
+
...generated content...
|
|
462
|
+
<!-- linear-tools:sync-end MARKER -->
|
|
463
|
+
|
|
464
|
+
Project Document Index:
|
|
465
|
+
A projectField target can set documentIndexMarker to maintain a managed list of synced document targets.
|
|
466
|
+
Use this for one overview doc in the project body plus separate linked Linear documents for deeper docs.
|
|
467
|
+
|
|
468
|
+
Examples:
|
|
469
|
+
pi-linear-tools sync-doc init --cwd /path/to/subproject --project "Project name or ID"
|
|
470
|
+
pi-linear-tools sync-doc explain
|
|
471
|
+
pi-linear-tools sync-doc list
|
|
472
|
+
pi-linear-tools sync-doc run
|
|
473
|
+
pi-linear-tools sync-doc check
|
|
474
|
+
pi-linear-tools sync-doc run --target project-overview
|
|
475
|
+
pi-linear-tools sync-doc check --target project-overview
|
|
476
|
+
pi-linear-tools sync-doc run --file README.md --project "Project name or ID" --field content
|
|
477
|
+
pi-linear-tools sync-doc run --file docs/provider.md --project "Project name or ID" --target-type document --document-title "Provider Doc"
|
|
478
|
+
`);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function printProjectUpdateHelp() {
|
|
482
|
+
console.log(`pi-linear-tools project-update - Manage Linear project updates
|
|
483
|
+
|
|
484
|
+
Usage:
|
|
485
|
+
pi-linear-tools project-update <action> [options]
|
|
486
|
+
|
|
487
|
+
Guidance:
|
|
488
|
+
This command manages the entries shown in a project's Updates tab.
|
|
489
|
+
Create an update by project name or ID, then use the returned project update ID for later view/update/archive actions.
|
|
265
490
|
|
|
266
491
|
Actions:
|
|
267
|
-
list
|
|
492
|
+
list List updates for a project
|
|
493
|
+
view View project update details
|
|
494
|
+
create Create a new project update
|
|
495
|
+
update Update an existing project update
|
|
496
|
+
archive Archive a project update
|
|
497
|
+
unarchive Restore an archived project update
|
|
498
|
+
|
|
499
|
+
List Options:
|
|
500
|
+
--project X Project name or ID (required)
|
|
501
|
+
--limit N Max results (default: 10)
|
|
502
|
+
--include-archived X true or false
|
|
503
|
+
|
|
504
|
+
View Options:
|
|
505
|
+
<project-update-id> Project update ID
|
|
506
|
+
|
|
507
|
+
Create Options:
|
|
508
|
+
--project X Project name or ID (required)
|
|
509
|
+
--body X Update body in markdown
|
|
510
|
+
--health X onTrack, atRisk, or offTrack
|
|
511
|
+
--is-diff-hidden X true or false
|
|
512
|
+
|
|
513
|
+
Update Options:
|
|
514
|
+
<project-update-id> Project update ID
|
|
515
|
+
--body X Updated body in markdown
|
|
516
|
+
--health X onTrack, atRisk, or offTrack
|
|
517
|
+
--is-diff-hidden X true or false
|
|
518
|
+
|
|
519
|
+
Archive Options:
|
|
520
|
+
<project-update-id> Project update ID
|
|
521
|
+
|
|
522
|
+
Unarchive Options:
|
|
523
|
+
<project-update-id> Project update ID
|
|
524
|
+
|
|
525
|
+
Examples:
|
|
526
|
+
pi-linear-tools project-update list --project "Roadmap Refresh"
|
|
527
|
+
pi-linear-tools project-update create --project "Roadmap Refresh" --body "Weekly update" --health onTrack
|
|
528
|
+
pi-linear-tools project-update update 12345678-1234-1234-1234-123456789abc --body "Revised update" --health atRisk
|
|
529
|
+
pi-linear-tools project-update view 12345678-1234-1234-1234-123456789abc
|
|
268
530
|
`);
|
|
269
531
|
}
|
|
270
532
|
|
|
@@ -557,6 +819,24 @@ async function handleIssueView(args) {
|
|
|
557
819
|
console.log(result.content[0].text);
|
|
558
820
|
}
|
|
559
821
|
|
|
822
|
+
async function handleIssueActivity(args) {
|
|
823
|
+
const client = await createAuthenticatedClient();
|
|
824
|
+
|
|
825
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
826
|
+
if (positional.length === 0) {
|
|
827
|
+
throw new Error('Missing required argument: issue key or ID');
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const params = {
|
|
831
|
+
issue: positional[0],
|
|
832
|
+
limit: parseNumber(readFlag(args, '--limit')),
|
|
833
|
+
includeArchived: parseBoolean(readFlag(args, '--include-archived')),
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
const result = await executeIssueActivity(client, params);
|
|
837
|
+
console.log(result.content[0].text);
|
|
838
|
+
}
|
|
839
|
+
|
|
560
840
|
async function handleIssueCreate(args) {
|
|
561
841
|
const client = await createAuthenticatedClient();
|
|
562
842
|
|
|
@@ -670,6 +950,8 @@ async function handleIssue(args) {
|
|
|
670
950
|
return handleIssueList(rest);
|
|
671
951
|
case 'view':
|
|
672
952
|
return handleIssueView(rest);
|
|
953
|
+
case 'activity':
|
|
954
|
+
return handleIssueActivity(rest);
|
|
673
955
|
case 'create':
|
|
674
956
|
return handleIssueCreate(rest);
|
|
675
957
|
case 'update':
|
|
@@ -694,6 +976,107 @@ async function handleProjectList() {
|
|
|
694
976
|
console.log(result.content[0].text);
|
|
695
977
|
}
|
|
696
978
|
|
|
979
|
+
async function handleProjectView(args) {
|
|
980
|
+
const client = await createAuthenticatedClient();
|
|
981
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
982
|
+
if (positional.length === 0) {
|
|
983
|
+
throw new Error('Missing required argument: project name or ID');
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
const result = await executeProjectView(client, {
|
|
987
|
+
project: positional[0],
|
|
988
|
+
});
|
|
989
|
+
console.log(result.content[0].text);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
async function handleProjectCreate(args) {
|
|
993
|
+
const client = await createAuthenticatedClient();
|
|
994
|
+
const params = {
|
|
995
|
+
name: readFlag(args, '--name'),
|
|
996
|
+
teams: parseArrayValue(readFlag(args, '--teams')),
|
|
997
|
+
description: readFlag(args, '--description'),
|
|
998
|
+
lead: readFlag(args, '--lead'),
|
|
999
|
+
priority: parseNumber(readFlag(args, '--priority')),
|
|
1000
|
+
color: readFlag(args, '--color'),
|
|
1001
|
+
icon: readFlag(args, '--icon'),
|
|
1002
|
+
startDate: readFlag(args, '--start-date'),
|
|
1003
|
+
targetDate: readFlag(args, '--target-date'),
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
if (!params.name) {
|
|
1007
|
+
throw new Error('Missing required flag: --name');
|
|
1008
|
+
}
|
|
1009
|
+
if (!params.teams || params.teams.length === 0) {
|
|
1010
|
+
throw new Error('Missing required flag: --teams');
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const result = await executeProjectCreate(client, params);
|
|
1014
|
+
console.log(result.content[0].text);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
async function handleProjectUpdate(args) {
|
|
1018
|
+
const client = await createAuthenticatedClient();
|
|
1019
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
1020
|
+
if (positional.length === 0) {
|
|
1021
|
+
throw new Error('Missing required argument: project name or ID');
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
const params = {
|
|
1025
|
+
project: positional[0],
|
|
1026
|
+
name: readFlag(args, '--name'),
|
|
1027
|
+
teams: parseArrayValue(readFlag(args, '--teams')),
|
|
1028
|
+
description: readFlag(args, '--description'),
|
|
1029
|
+
lead: readFlag(args, '--lead'),
|
|
1030
|
+
priority: parseNumber(readFlag(args, '--priority')),
|
|
1031
|
+
color: readFlag(args, '--color'),
|
|
1032
|
+
icon: readFlag(args, '--icon'),
|
|
1033
|
+
startDate: readFlag(args, '--start-date'),
|
|
1034
|
+
targetDate: readFlag(args, '--target-date'),
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
const result = await executeProjectUpdate(client, params);
|
|
1038
|
+
console.log(result.content[0].text);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
async function handleProjectDelete(args) {
|
|
1042
|
+
const client = await createAuthenticatedClient();
|
|
1043
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
1044
|
+
if (positional.length === 0) {
|
|
1045
|
+
throw new Error('Missing required argument: project name or ID');
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const result = await executeProjectDelete(client, {
|
|
1049
|
+
project: positional[0],
|
|
1050
|
+
});
|
|
1051
|
+
console.log(result.content[0].text);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
async function handleProjectArchive(args) {
|
|
1055
|
+
const client = await createAuthenticatedClient();
|
|
1056
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
1057
|
+
if (positional.length === 0) {
|
|
1058
|
+
throw new Error('Missing required argument: project name or ID');
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
const result = await executeProjectArchive(client, {
|
|
1062
|
+
project: positional[0],
|
|
1063
|
+
});
|
|
1064
|
+
console.log(result.content[0].text);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
async function handleProjectUnarchive(args) {
|
|
1068
|
+
const client = await createAuthenticatedClient();
|
|
1069
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
1070
|
+
if (positional.length === 0) {
|
|
1071
|
+
throw new Error('Missing required argument: project name or ID');
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
const result = await executeProjectUnarchive(client, {
|
|
1075
|
+
project: positional[0],
|
|
1076
|
+
});
|
|
1077
|
+
console.log(result.content[0].text);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
697
1080
|
async function handleProject(args) {
|
|
698
1081
|
const [action] = args;
|
|
699
1082
|
|
|
@@ -705,11 +1088,312 @@ async function handleProject(args) {
|
|
|
705
1088
|
switch (action) {
|
|
706
1089
|
case 'list':
|
|
707
1090
|
return handleProjectList();
|
|
1091
|
+
case 'view':
|
|
1092
|
+
return handleProjectView(args.slice(1));
|
|
1093
|
+
case 'create':
|
|
1094
|
+
return handleProjectCreate(args.slice(1));
|
|
1095
|
+
case 'update':
|
|
1096
|
+
return handleProjectUpdate(args.slice(1));
|
|
1097
|
+
case 'delete':
|
|
1098
|
+
return handleProjectDelete(args.slice(1));
|
|
1099
|
+
case 'archive':
|
|
1100
|
+
return handleProjectArchive(args.slice(1));
|
|
1101
|
+
case 'unarchive':
|
|
1102
|
+
return handleProjectUnarchive(args.slice(1));
|
|
708
1103
|
default:
|
|
709
1104
|
throw new Error(`Unknown project action: ${action}`);
|
|
710
1105
|
}
|
|
711
1106
|
}
|
|
712
1107
|
|
|
1108
|
+
// ===== PROJECT UPDATE HANDLERS =====
|
|
1109
|
+
|
|
1110
|
+
async function handleProjectUpdateListCli(args) {
|
|
1111
|
+
const client = await createAuthenticatedClient();
|
|
1112
|
+
const params = {
|
|
1113
|
+
project: readFlag(args, '--project'),
|
|
1114
|
+
limit: parseNumber(readFlag(args, '--limit')),
|
|
1115
|
+
includeArchived: parseBoolean(readFlag(args, '--include-archived')),
|
|
1116
|
+
};
|
|
1117
|
+
|
|
1118
|
+
if (!params.project) {
|
|
1119
|
+
throw new Error('Missing required flag: --project');
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
const result = await executeProjectUpdateList(client, params);
|
|
1123
|
+
console.log(result.content[0].text);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
function printSyncDocResult(result) {
|
|
1127
|
+
if (!result.changed) {
|
|
1128
|
+
if (result.targetType === 'document') {
|
|
1129
|
+
console.log(`No sync changes needed for document "${result.entityName}" from ${result.file}`);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
console.log(`No sync changes needed for ${result.entityType} "${result.entityName}" field "${result.field}" from ${result.file}`);
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
if (result.mode === 'check') {
|
|
1137
|
+
if (result.targetType === 'document') {
|
|
1138
|
+
console.log(`Sync needed for document "${result.entityName}" from ${result.file}`);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
console.log(`Sync needed for ${result.entityType} "${result.entityName}" field "${result.field}" from ${result.file}`);
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
if (result.targetType === 'document') {
|
|
1146
|
+
console.log(`Synced ${result.file} to document "${result.entityName}" using marker "${result.marker}"`);
|
|
1147
|
+
if (result.documentUrl) {
|
|
1148
|
+
console.log(result.documentUrl);
|
|
1149
|
+
}
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
console.log(`Synced ${result.file} to ${result.entityType} "${result.entityName}" field "${result.field}" using marker "${result.marker}"`);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function printSyncDocTargets(result) {
|
|
1157
|
+
if (!Array.isArray(result.targets) || result.targets.length === 0) {
|
|
1158
|
+
console.log('No sync-doc targets configured.');
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
const lines = result.targets.map((target) => {
|
|
1163
|
+
if (target.targetType === 'document') {
|
|
1164
|
+
const entityLabel = `${target.issue ? 'issue' : 'project'}:${target.entityRef}`;
|
|
1165
|
+
return `- ${target.name} -> document "${target.title}" linked to ${entityLabel} from ${target.file} (marker: ${target.marker})`;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
const entityLabel = `${target.targetType === 'issueField' ? 'issue' : 'project'}:${target.entityRef}`;
|
|
1169
|
+
const extra = target.documentIndexMarker ? `, document index: ${target.documentIndexMarker}` : '';
|
|
1170
|
+
return `- ${target.name} -> ${entityLabel} field "${target.field}" from ${target.file} (marker: ${target.marker}${extra})`;
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
if (result.configPath) {
|
|
1174
|
+
console.log(`Resolved sync-doc targets from ${result.configPath}`);
|
|
1175
|
+
}
|
|
1176
|
+
console.log(lines.join('\n'));
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
function printSyncDocBatchResult(result) {
|
|
1180
|
+
const header = result.mode === 'check'
|
|
1181
|
+
? `Checked ${result.total} sync-doc target(s): ${result.changedCount} need updates, ${result.unchangedCount} unchanged`
|
|
1182
|
+
: `Ran ${result.total} sync-doc target(s): ${result.changedCount} updated, ${result.unchangedCount} unchanged`;
|
|
1183
|
+
const lines = result.results.map((entry) => {
|
|
1184
|
+
const status = entry.changed ? (result.mode === 'check' ? 'needs-sync' : 'synced') : 'unchanged';
|
|
1185
|
+
if (entry.targetType === 'document') {
|
|
1186
|
+
return `- [${status}] ${entry.targetName} -> document "${entry.entityName}" from ${entry.file}`;
|
|
1187
|
+
}
|
|
1188
|
+
return `- [${status}] ${entry.targetName} -> ${entry.entityType} "${entry.entityName}" field "${entry.field}" from ${entry.file}`;
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
console.log([header, ...lines].join('\n'));
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
function printSyncDocInitResult(result) {
|
|
1195
|
+
if (!result.created) {
|
|
1196
|
+
console.log(`Sync-doc config already exists at ${result.configPath}`);
|
|
1197
|
+
console.log('Use --force to overwrite it, or edit the existing config manually.');
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
console.log(`${result.overwritten ? 'Rewrote' : 'Created'} ${result.configPath}`);
|
|
1202
|
+
console.log(`State will be written to ${result.statePath}`);
|
|
1203
|
+
if (result.target) {
|
|
1204
|
+
console.log(`Starter target: ${result.target.name} -> project:${result.target.project} field "${result.target.field}" from ${result.target.file}`);
|
|
1205
|
+
}
|
|
1206
|
+
console.log('Next steps:');
|
|
1207
|
+
console.log(`- Edit ${result.configPath} to add document targets for deeper docs`);
|
|
1208
|
+
console.log(`- Run pi-linear-tools sync-doc list --cwd ${result.cwd}`);
|
|
1209
|
+
console.log(`- Run pi-linear-tools sync-doc run --cwd ${result.cwd}`);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
async function handleSyncDocCommand(args) {
|
|
1213
|
+
const [maybeAction, ...restArgs] = args;
|
|
1214
|
+
const action = !maybeAction || maybeAction.startsWith('-') ? 'run' : maybeAction;
|
|
1215
|
+
const commandArgs = !maybeAction || maybeAction.startsWith('-') ? args : restArgs;
|
|
1216
|
+
const cwd = readFlag(commandArgs, '--cwd') || process.cwd();
|
|
1217
|
+
|
|
1218
|
+
if (
|
|
1219
|
+
action === '--help'
|
|
1220
|
+
|| action === '-h'
|
|
1221
|
+
|| action === 'help'
|
|
1222
|
+
|| hasFlag(commandArgs, '--help')
|
|
1223
|
+
|| hasFlag(commandArgs, '-h')
|
|
1224
|
+
) {
|
|
1225
|
+
printSyncDocHelp();
|
|
1226
|
+
return;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
if (!['init', 'explain', 'list', 'run', 'check'].includes(action)) {
|
|
1230
|
+
throw new Error(`Unknown sync-doc action: ${action}`);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
if (action === 'explain') {
|
|
1234
|
+
console.log(explainSyncDocSetup());
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
if (action === 'init') {
|
|
1239
|
+
const result = await initSyncDocConfig({
|
|
1240
|
+
cwd,
|
|
1241
|
+
file: readFlag(commandArgs, '--file'),
|
|
1242
|
+
project: readFlag(commandArgs, '--project'),
|
|
1243
|
+
field: readFlag(commandArgs, '--field'),
|
|
1244
|
+
marker: readFlag(commandArgs, '--marker'),
|
|
1245
|
+
name: readFlag(commandArgs, '--name'),
|
|
1246
|
+
documentIndexMarker: readFlag(commandArgs, '--document-index-marker'),
|
|
1247
|
+
force: hasFlag(commandArgs, '--force'),
|
|
1248
|
+
});
|
|
1249
|
+
printSyncDocInitResult(result);
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
if (action === 'list') {
|
|
1254
|
+
const result = await listSyncDocTargets({
|
|
1255
|
+
cwd,
|
|
1256
|
+
configPath: readFlag(commandArgs, '--config'),
|
|
1257
|
+
});
|
|
1258
|
+
printSyncDocTargets(result);
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
const client = await createAuthenticatedClient();
|
|
1263
|
+
const hasOneOffFlags = Boolean(
|
|
1264
|
+
readFlag(commandArgs, '--file')
|
|
1265
|
+
&& (readFlag(commandArgs, '--project') || readFlag(commandArgs, '--issue'))
|
|
1266
|
+
);
|
|
1267
|
+
const hasNamedTarget = Boolean(readFlag(commandArgs, '--target'));
|
|
1268
|
+
|
|
1269
|
+
if (!hasOneOffFlags && !hasNamedTarget) {
|
|
1270
|
+
const result = await runAllSyncDocs(client, {
|
|
1271
|
+
mode: action,
|
|
1272
|
+
cwd,
|
|
1273
|
+
configPath: readFlag(commandArgs, '--config'),
|
|
1274
|
+
});
|
|
1275
|
+
printSyncDocBatchResult(result);
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
const result = await runSyncDoc(client, {
|
|
1280
|
+
mode: action,
|
|
1281
|
+
cwd,
|
|
1282
|
+
configPath: readFlag(commandArgs, '--config'),
|
|
1283
|
+
targetName: readFlag(commandArgs, '--target'),
|
|
1284
|
+
file: readFlag(commandArgs, '--file'),
|
|
1285
|
+
project: readFlag(commandArgs, '--project'),
|
|
1286
|
+
issue: readFlag(commandArgs, '--issue'),
|
|
1287
|
+
targetType: readFlag(commandArgs, '--target-type'),
|
|
1288
|
+
documentTitle: readFlag(commandArgs, '--document-title'),
|
|
1289
|
+
documentId: readFlag(commandArgs, '--document-id'),
|
|
1290
|
+
field: readFlag(commandArgs, '--field'),
|
|
1291
|
+
marker: readFlag(commandArgs, '--marker'),
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
printSyncDocResult(result);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
async function handleProjectUpdateViewCli(args) {
|
|
1298
|
+
const client = await createAuthenticatedClient();
|
|
1299
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
1300
|
+
if (positional.length === 0) {
|
|
1301
|
+
throw new Error('Missing required argument: project update ID');
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
const result = await executeProjectUpdateView(client, {
|
|
1305
|
+
projectUpdate: positional[0],
|
|
1306
|
+
});
|
|
1307
|
+
console.log(result.content[0].text);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
async function handleProjectUpdateCreateCli(args) {
|
|
1311
|
+
const client = await createAuthenticatedClient();
|
|
1312
|
+
const params = {
|
|
1313
|
+
project: readFlag(args, '--project'),
|
|
1314
|
+
body: readFlag(args, '--body'),
|
|
1315
|
+
health: readFlag(args, '--health'),
|
|
1316
|
+
isDiffHidden: parseBoolean(readFlag(args, '--is-diff-hidden')),
|
|
1317
|
+
};
|
|
1318
|
+
|
|
1319
|
+
if (!params.project) {
|
|
1320
|
+
throw new Error('Missing required flag: --project');
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
const result = await executeProjectUpdateCreate(client, params);
|
|
1324
|
+
console.log(result.content[0].text);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
async function handleProjectUpdateUpdateCli(args) {
|
|
1328
|
+
const client = await createAuthenticatedClient();
|
|
1329
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
1330
|
+
if (positional.length === 0) {
|
|
1331
|
+
throw new Error('Missing required argument: project update ID');
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
const params = {
|
|
1335
|
+
projectUpdate: positional[0],
|
|
1336
|
+
body: readFlag(args, '--body'),
|
|
1337
|
+
health: readFlag(args, '--health'),
|
|
1338
|
+
isDiffHidden: parseBoolean(readFlag(args, '--is-diff-hidden')),
|
|
1339
|
+
};
|
|
1340
|
+
|
|
1341
|
+
const result = await executeProjectUpdateUpdate(client, params);
|
|
1342
|
+
console.log(result.content[0].text);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
async function handleProjectUpdateArchiveCli(args) {
|
|
1346
|
+
const client = await createAuthenticatedClient();
|
|
1347
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
1348
|
+
if (positional.length === 0) {
|
|
1349
|
+
throw new Error('Missing required argument: project update ID');
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
const result = await executeProjectUpdateArchive(client, {
|
|
1353
|
+
projectUpdate: positional[0],
|
|
1354
|
+
});
|
|
1355
|
+
console.log(result.content[0].text);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
async function handleProjectUpdateUnarchiveCli(args) {
|
|
1359
|
+
const client = await createAuthenticatedClient();
|
|
1360
|
+
const positional = args.filter((a) => !a.startsWith('-'));
|
|
1361
|
+
if (positional.length === 0) {
|
|
1362
|
+
throw new Error('Missing required argument: project update ID');
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
const result = await executeProjectUpdateUnarchive(client, {
|
|
1366
|
+
projectUpdate: positional[0],
|
|
1367
|
+
});
|
|
1368
|
+
console.log(result.content[0].text);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
async function handleProjectUpdateCommand(args) {
|
|
1372
|
+
const [action] = args;
|
|
1373
|
+
|
|
1374
|
+
if (!action || action === '--help' || action === '-h') {
|
|
1375
|
+
printProjectUpdateHelp();
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
switch (action) {
|
|
1380
|
+
case 'list':
|
|
1381
|
+
return handleProjectUpdateListCli(args.slice(1));
|
|
1382
|
+
case 'view':
|
|
1383
|
+
return handleProjectUpdateViewCli(args.slice(1));
|
|
1384
|
+
case 'create':
|
|
1385
|
+
return handleProjectUpdateCreateCli(args.slice(1));
|
|
1386
|
+
case 'update':
|
|
1387
|
+
return handleProjectUpdateUpdateCli(args.slice(1));
|
|
1388
|
+
case 'archive':
|
|
1389
|
+
return handleProjectUpdateArchiveCli(args.slice(1));
|
|
1390
|
+
case 'unarchive':
|
|
1391
|
+
return handleProjectUpdateUnarchiveCli(args.slice(1));
|
|
1392
|
+
default:
|
|
1393
|
+
throw new Error(`Unknown project-update action: ${action}`);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
713
1397
|
// ===== TEAM HANDLERS =====
|
|
714
1398
|
|
|
715
1399
|
async function handleTeamList() {
|
|
@@ -877,6 +1561,16 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
877
1561
|
return;
|
|
878
1562
|
}
|
|
879
1563
|
|
|
1564
|
+
if (command === 'project-update') {
|
|
1565
|
+
await handleProjectUpdateCommand(rest);
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
if (command === 'sync-doc') {
|
|
1570
|
+
await handleSyncDocCommand(rest);
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
880
1574
|
if (command === 'team') {
|
|
881
1575
|
await handleTeam(rest);
|
|
882
1576
|
return;
|