@elnora-ai/linear 1.0.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/.claude-plugin/marketplace.json +19 -0
- package/.claude-plugin/plugin.json +19 -0
- package/CHANGELOG.md +29 -0
- package/LICENSE +201 -0
- package/README.md +61 -0
- package/agents/linear-issue-creator.md +45 -0
- package/agents/linear-issue-reviewer.md +44 -0
- package/agents/linear-issue-updater.md +45 -0
- package/agents/linear-url-to-issues.md +51 -0
- package/commands/linear-bulk.md +42 -0
- package/commands/linear-cleanup.md +45 -0
- package/commands/linear-curator-run.md +39 -0
- package/commands/linear-my-issues.md +25 -0
- package/commands/linear-search.md +32 -0
- package/commands/linear-sync.md +54 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +126 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/auth.d.ts +21 -0
- package/dist/client/auth.d.ts.map +1 -0
- package/dist/client/auth.js +74 -0
- package/dist/client/auth.js.map +1 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/linear-client.d.ts +5 -0
- package/dist/client/linear-client.d.ts.map +1 -0
- package/dist/client/linear-client.js +18 -0
- package/dist/client/linear-client.js.map +1 -0
- package/dist/commands/bulk.d.ts +39 -0
- package/dist/commands/bulk.d.ts.map +1 -0
- package/dist/commands/bulk.js +103 -0
- package/dist/commands/bulk.js.map +1 -0
- package/dist/commands/cleanup.d.ts +39 -0
- package/dist/commands/cleanup.d.ts.map +1 -0
- package/dist/commands/cleanup.js +100 -0
- package/dist/commands/cleanup.js.map +1 -0
- package/dist/commands/curator.d.ts +23 -0
- package/dist/commands/curator.d.ts.map +1 -0
- package/dist/commands/curator.js +66 -0
- package/dist/commands/curator.js.map +1 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/my-issues.d.ts +7 -0
- package/dist/commands/my-issues.d.ts.map +1 -0
- package/dist/commands/my-issues.js +24 -0
- package/dist/commands/my-issues.js.map +1 -0
- package/dist/commands/search.d.ts +20 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +54 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/sync.d.ts +87 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +229 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +12 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +140 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/types.d.ts +133 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +13 -0
- package/dist/config/types.js.map +1 -0
- package/dist/output/formatter.d.ts +16 -0
- package/dist/output/formatter.d.ts.map +1 -0
- package/dist/output/formatter.js +34 -0
- package/dist/output/formatter.js.map +1 -0
- package/dist/output/index.d.ts +2 -0
- package/dist/output/index.d.ts.map +1 -0
- package/dist/output/index.js +2 -0
- package/dist/output/index.js.map +1 -0
- package/dist/signals/external-command.d.ts +30 -0
- package/dist/signals/external-command.d.ts.map +1 -0
- package/dist/signals/external-command.js +96 -0
- package/dist/signals/external-command.js.map +1 -0
- package/dist/signals/index.d.ts +4 -0
- package/dist/signals/index.d.ts.map +1 -0
- package/dist/signals/index.js +4 -0
- package/dist/signals/index.js.map +1 -0
- package/dist/signals/registry.d.ts +5 -0
- package/dist/signals/registry.d.ts.map +1 -0
- package/dist/signals/registry.js +29 -0
- package/dist/signals/registry.js.map +1 -0
- package/dist/signals/types.d.ts +25 -0
- package/dist/signals/types.d.ts.map +1 -0
- package/dist/signals/types.js +9 -0
- package/dist/signals/types.js.map +1 -0
- package/package.json +76 -0
- package/references/projects.example.json +26 -0
- package/references/projects.placeholder.json +6 -0
- package/references/repos.example.json +21 -0
- package/references/repos.placeholder.json +6 -0
- package/references/signal-sources.example.json +42 -0
- package/references/signal-sources.placeholder.json +6 -0
- package/references/slack.example.json +10 -0
- package/references/slack.placeholder.json +8 -0
- package/references/teams.example.json +21 -0
- package/references/teams.placeholder.json +6 -0
- package/references/users.example.json +18 -0
- package/references/users.placeholder.json +6 -0
- package/references/workflows.example.json +44 -0
- package/references/workflows.placeholder.json +7 -0
- package/schemas/projects.json +47 -0
- package/schemas/repos.json +41 -0
- package/schemas/signal-sources.json +90 -0
- package/schemas/slack.json +45 -0
- package/schemas/teams.json +43 -0
- package/schemas/users.json +41 -0
- package/schemas/workflows.json +61 -0
- package/skills/linear-workspace/SKILL.md +52 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: linear-my-issues
|
|
3
|
+
description: List Linear issues assigned to you, grouped by state
|
|
4
|
+
allowed-tools: Bash, Read
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# My Linear Issues
|
|
8
|
+
|
|
9
|
+
List every Linear issue currently assigned to the viewer.
|
|
10
|
+
|
|
11
|
+
## Run
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
elnora-linear my-issues --limit 50 --output json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Present
|
|
18
|
+
|
|
19
|
+
Group by state in this order: `In Progress`, `Todo`, `Backlog`, `Done` (last 7 days only), `Canceled` (hide unless asked). Within each state, sort by `updatedAt` descending. Show identifier, title, team, project.
|
|
20
|
+
|
|
21
|
+
If the list is empty, say so cleanly: "Nothing assigned." Don't fabricate.
|
|
22
|
+
|
|
23
|
+
## Don't
|
|
24
|
+
|
|
25
|
+
- Don't include closed/cancelled issues older than 7 days — the user wants their active load
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: linear-search
|
|
3
|
+
description: Search Linear issues by keyword, team, assignee, or state
|
|
4
|
+
argument-hint: "<query>"
|
|
5
|
+
allowed-tools: Bash, Read
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Linear Search
|
|
9
|
+
|
|
10
|
+
Search Linear issues for: **{{query}}**
|
|
11
|
+
|
|
12
|
+
## Run
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
elnora-linear search --query "{{query}}" --limit 25 --output json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
For more focused queries, add flags:
|
|
19
|
+
- `--team ENG` — restrict to a team
|
|
20
|
+
- `--assignee me` — only your issues (or `--assignee "Alice Smith"`)
|
|
21
|
+
- `--state "In Progress"` — by workflow state
|
|
22
|
+
- `--limit <n>` — default 25
|
|
23
|
+
- `--output text` — for human-readable; default here is JSON for parsing
|
|
24
|
+
|
|
25
|
+
## Present
|
|
26
|
+
|
|
27
|
+
Render the JSON as a table: identifier, state, assignee, title. Highlight any issue whose state matches what the user is looking for.
|
|
28
|
+
|
|
29
|
+
## Don't
|
|
30
|
+
|
|
31
|
+
- Don't paginate; the `--limit` flag controls volume
|
|
32
|
+
- Don't apply mutations — that's `/linear-bulk`, `/linear-cleanup`, or the `linear-issue-updater` agent
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: linear-sync
|
|
3
|
+
description: Populate or refresh the Linear reference files (teams, projects, users, workflows)
|
|
4
|
+
allowed-tools: Bash, Read
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Linear Sync
|
|
8
|
+
|
|
9
|
+
Refresh the reference files the rest of `elnora-linear` reads — teams, projects, users, workflow states. Run this on first install and any time your Linear workspace changes (new team, renamed project, etc.).
|
|
10
|
+
|
|
11
|
+
## Run
|
|
12
|
+
|
|
13
|
+
The fast path: refresh everything auto-discoverable from the Linear API.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
elnora-linear sync all
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Pick one target:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
elnora-linear sync teams
|
|
23
|
+
elnora-linear sync projects
|
|
24
|
+
elnora-linear sync users
|
|
25
|
+
elnora-linear sync workflows
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Validate references:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
elnora-linear sync verify
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Reports each reference as `user-file` (populated) or `placeholder` (default, empty).
|
|
35
|
+
|
|
36
|
+
Import an existing config bundle:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
elnora-linear sync import --from /path/to/bundle.json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Where it writes
|
|
43
|
+
|
|
44
|
+
In precedence order:
|
|
45
|
+
1. `--references-dir <path>` flag
|
|
46
|
+
2. `LINEAR_REFERENCES_DIR` env var
|
|
47
|
+
3. `~/.config/elnora-linear/` (auto-created if needed)
|
|
48
|
+
|
|
49
|
+
Never writes to the bundled defaults in the installed package — those stay clean.
|
|
50
|
+
|
|
51
|
+
## Don't
|
|
52
|
+
|
|
53
|
+
- Don't run `sync all` repeatedly in a loop — it issues N×teams API calls
|
|
54
|
+
- Don't commit the populated reference files to a public repo — they're user-specific data
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// elnora-linear CLI entrypoint.
|
|
3
|
+
//
|
|
4
|
+
// Commander-based subcommand dispatcher. Each subcommand is a thin wrapper
|
|
5
|
+
// over a function in src/commands/*; the heavy lifting (auth, API calls,
|
|
6
|
+
// formatting) happens there.
|
|
7
|
+
import { Command, Option } from "commander";
|
|
8
|
+
import { runBulk } from "./commands/bulk.js";
|
|
9
|
+
import { runCleanup } from "./commands/cleanup.js";
|
|
10
|
+
import { runCurator } from "./commands/curator.js";
|
|
11
|
+
import { runMyIssues } from "./commands/my-issues.js";
|
|
12
|
+
import { runSearch } from "./commands/search.js";
|
|
13
|
+
import { AUTO_SYNC_TARGETS, runSyncAll, runSyncImport, runSyncTarget, runSyncVerify, } from "./commands/sync.js";
|
|
14
|
+
const VERSION = "0.0.0";
|
|
15
|
+
const program = new Command()
|
|
16
|
+
.name("elnora-linear")
|
|
17
|
+
.description("Linear workspace CLI for Claude Code — search, bulk edit, agents, config-driven curator.")
|
|
18
|
+
.version(VERSION, "-v, --version", "Print version")
|
|
19
|
+
.helpOption("-h, --help", "Show this help");
|
|
20
|
+
program
|
|
21
|
+
.command("search")
|
|
22
|
+
.description("Search Linear issues")
|
|
23
|
+
.option("-q, --query <text>", "Search text (matches issue title + description)")
|
|
24
|
+
.option("-t, --team <key>", "Restrict to team key, e.g. ENG")
|
|
25
|
+
.option("-a, --assignee <name>", "Assignee name, email, or 'me'")
|
|
26
|
+
.option("-s, --state <name>", "Workflow state name, e.g. 'In Progress'")
|
|
27
|
+
.option("-l, --limit <n>", "Max results", (v) => Number.parseInt(v, 10), 25)
|
|
28
|
+
.option("-o, --output <mode>", "Output mode: text or json", "text")
|
|
29
|
+
.action(async (opts) => {
|
|
30
|
+
await runSearch(opts);
|
|
31
|
+
});
|
|
32
|
+
program
|
|
33
|
+
.command("my-issues")
|
|
34
|
+
.description("List issues assigned to you")
|
|
35
|
+
.option("-l, --limit <n>", "Max results", (v) => Number.parseInt(v, 10), 25)
|
|
36
|
+
.option("-o, --output <mode>", "Output mode: text or json", "text")
|
|
37
|
+
.action(async (opts) => {
|
|
38
|
+
await runMyIssues(opts);
|
|
39
|
+
});
|
|
40
|
+
program
|
|
41
|
+
.command("bulk")
|
|
42
|
+
.description("Apply the same change to many issues. Default is dry-run; pass --yes to commit.")
|
|
43
|
+
.option("-q, --query <text>", "Filter: search text")
|
|
44
|
+
.option("-t, --team <key>", "Filter: team key")
|
|
45
|
+
.option("-a, --assignee <name>", "Filter: assignee name, email, or 'me'")
|
|
46
|
+
.option("-s, --state <name>", "Filter: workflow state name")
|
|
47
|
+
.option("--set-state <name>", "Mutation: move matching issues to this state")
|
|
48
|
+
.option("--add-comment <text>", "Mutation: add this comment to each matching issue")
|
|
49
|
+
.option("-l, --limit <n>", "Max issues to touch", (v) => Number.parseInt(v, 10), 100)
|
|
50
|
+
.option("-y, --yes", "Commit the mutations (default is dry-run)", false)
|
|
51
|
+
.option("-o, --output <mode>", "Output mode: text or json", "text")
|
|
52
|
+
.action(async (opts) => {
|
|
53
|
+
await runBulk(opts);
|
|
54
|
+
});
|
|
55
|
+
program
|
|
56
|
+
.command("cleanup")
|
|
57
|
+
.description("Find stale issues and act on them. Default is dry-run; pass --yes to commit.")
|
|
58
|
+
.option("-t, --team <key>", "Filter: team key")
|
|
59
|
+
.option("-s, --states <names>", 'Filter: comma-separated state names (default "Todo,Backlog")', (v) => v.split(",").map((s) => s.trim()))
|
|
60
|
+
.option("--inactive-days <n>", "Issues with no activity for at least N days", (v) => Number.parseInt(v, 10), 30)
|
|
61
|
+
.addOption(new Option("--action <action>", "What to do with stale issues")
|
|
62
|
+
.choices(["close", "cancel", "comment"])
|
|
63
|
+
.default("comment"))
|
|
64
|
+
.option("--message <text>", "Comment text (overrides default cleanup message)")
|
|
65
|
+
.option("-l, --limit <n>", "Max issues to consider", (v) => Number.parseInt(v, 10), 100)
|
|
66
|
+
.option("-y, --yes", "Commit the mutations (default is dry-run)", false)
|
|
67
|
+
.option("-o, --output <mode>", "Output mode: text or json", "text")
|
|
68
|
+
.action(async (opts) => {
|
|
69
|
+
await runCleanup(opts);
|
|
70
|
+
});
|
|
71
|
+
const sync = program
|
|
72
|
+
.command("sync")
|
|
73
|
+
.description("Populate or refresh reference files. Run `elnora-linear sync --help` for subcommands.");
|
|
74
|
+
sync
|
|
75
|
+
.command("all")
|
|
76
|
+
.description("Refresh every auto-discoverable target (teams, projects, users, workflows)")
|
|
77
|
+
.option("--references-dir <path>", "Override default references directory")
|
|
78
|
+
.option("-o, --output <mode>", "Output mode: text or json", "text")
|
|
79
|
+
.action(async (opts) => {
|
|
80
|
+
await runSyncAll(opts);
|
|
81
|
+
});
|
|
82
|
+
for (const target of AUTO_SYNC_TARGETS) {
|
|
83
|
+
sync
|
|
84
|
+
.command(target)
|
|
85
|
+
.description(`Fetch ${target} from the Linear API and write references/${target}.json`)
|
|
86
|
+
.option("--references-dir <path>", "Override default references directory")
|
|
87
|
+
.option("-o, --output <mode>", "Output mode: text or json", "text")
|
|
88
|
+
.action(async (opts) => {
|
|
89
|
+
await runSyncTarget(target, opts);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
sync
|
|
93
|
+
.command("verify")
|
|
94
|
+
.description("Validate every reference file against its schema; report which are user-populated vs placeholder.")
|
|
95
|
+
.option("--references-dir <path>", "Override default references directory")
|
|
96
|
+
.option("-o, --output <mode>", "Output mode: text or json", "text")
|
|
97
|
+
.action((opts) => {
|
|
98
|
+
runSyncVerify(opts);
|
|
99
|
+
});
|
|
100
|
+
sync
|
|
101
|
+
.command("import")
|
|
102
|
+
.description("Import a JSON bundle (top-level keys = reference names) into individual reference files.")
|
|
103
|
+
.requiredOption("--from <path>", "Path to the bundle JSON file")
|
|
104
|
+
.option("--references-dir <path>", "Override default references directory")
|
|
105
|
+
.option("-o, --output <mode>", "Output mode: text or json", "text")
|
|
106
|
+
.action((opts) => {
|
|
107
|
+
runSyncImport(opts);
|
|
108
|
+
});
|
|
109
|
+
program
|
|
110
|
+
.command("curator-run")
|
|
111
|
+
.description("Collect signals from configured signal sources and report. (Rule engine coming in a follow-up.)")
|
|
112
|
+
.option("--source <name>", "Run only the named source (matches signal_sources[].name)")
|
|
113
|
+
.option("--references-dir <path>", "Override default references directory")
|
|
114
|
+
.option("-o, --output <mode>", "Output mode: text or json", "text")
|
|
115
|
+
.action(async (opts) => {
|
|
116
|
+
await runCurator(opts);
|
|
117
|
+
});
|
|
118
|
+
try {
|
|
119
|
+
await program.parseAsync(process.argv);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
123
|
+
process.stderr.write(`${message}\n`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare class AuthError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
declare function validateKey(key: string): string;
|
|
5
|
+
declare function readKeyFromEnvFile(path: string): string | null;
|
|
6
|
+
declare function saveKeyToEnvFile(key: string, path?: string): void;
|
|
7
|
+
export interface GetApiKeyOptions {
|
|
8
|
+
/** Override env-file path (default: ~/.config/elnora-linear/.env). */
|
|
9
|
+
envFilePath?: string;
|
|
10
|
+
/** If true and key isn't found, prompt user interactively (requires TTY). */
|
|
11
|
+
allowPrompt?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function getApiKey(opts?: GetApiKeyOptions): Promise<string>;
|
|
14
|
+
export declare const _internal: {
|
|
15
|
+
validateKey: typeof validateKey;
|
|
16
|
+
readKeyFromEnvFile: typeof readKeyFromEnvFile;
|
|
17
|
+
saveKeyToEnvFile: typeof saveKeyToEnvFile;
|
|
18
|
+
DEFAULT_ENV_FILE: string;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/client/auth.ts"],"names":[],"mappings":"AAcA,qBAAa,SAAU,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAI3B;AAKD,iBAAS,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMxC;AAED,iBAAS,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQvD;AAED,iBAAS,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,MAAyB,GAAG,IAAI,CAI5E;AAED,MAAM,WAAW,gBAAgB;IAChC,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAsB,SAAS,CAAC,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0B5E;AAGD,eAAO,MAAM,SAAS;;;;;CAA0E,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Linear API key resolution.
|
|
2
|
+
//
|
|
3
|
+
// Precedence (highest first):
|
|
4
|
+
// 1. LINEAR_API_KEY env var
|
|
5
|
+
// 2. ~/.config/elnora-linear/.env (or path passed via envFilePath)
|
|
6
|
+
// 3. Interactive prompt (only if allowPrompt: true and stdin is a TTY)
|
|
7
|
+
//
|
|
8
|
+
// All Linear keys must start with "lin_api_" — validated before return.
|
|
9
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import { createInterface } from "node:readline/promises";
|
|
13
|
+
export class AuthError extends Error {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "AuthError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const KEY_PREFIX = "lin_api_";
|
|
20
|
+
const DEFAULT_ENV_FILE = join(homedir(), ".config", "elnora-linear", ".env");
|
|
21
|
+
function validateKey(key) {
|
|
22
|
+
const trimmed = key.trim().replace(/^["']|["']$/g, "");
|
|
23
|
+
if (!trimmed.startsWith(KEY_PREFIX)) {
|
|
24
|
+
throw new AuthError(`Linear API key must start with "${KEY_PREFIX}". Got: ${trimmed.slice(0, 12)}…`);
|
|
25
|
+
}
|
|
26
|
+
return trimmed;
|
|
27
|
+
}
|
|
28
|
+
function readKeyFromEnvFile(path) {
|
|
29
|
+
if (!existsSync(path))
|
|
30
|
+
return null;
|
|
31
|
+
const content = readFileSync(path, "utf8");
|
|
32
|
+
for (const line of content.split("\n")) {
|
|
33
|
+
const match = line.match(/^LINEAR_API_KEY\s*=\s*(.+?)\s*$/);
|
|
34
|
+
if (match)
|
|
35
|
+
return match[1];
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
function saveKeyToEnvFile(key, path = DEFAULT_ENV_FILE) {
|
|
40
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
41
|
+
writeFileSync(path, `LINEAR_API_KEY=${key}\n`, { mode: 0o600 });
|
|
42
|
+
chmodSync(path, 0o600);
|
|
43
|
+
}
|
|
44
|
+
export async function getApiKey(opts = {}) {
|
|
45
|
+
if (process.env.LINEAR_API_KEY) {
|
|
46
|
+
return validateKey(process.env.LINEAR_API_KEY);
|
|
47
|
+
}
|
|
48
|
+
const envFile = opts.envFilePath ?? DEFAULT_ENV_FILE;
|
|
49
|
+
const fileKey = readKeyFromEnvFile(envFile);
|
|
50
|
+
if (fileKey)
|
|
51
|
+
return validateKey(fileKey);
|
|
52
|
+
if (!opts.allowPrompt) {
|
|
53
|
+
throw new AuthError(`Linear API key not found. Set LINEAR_API_KEY env var, or place it in ${envFile} (mode 0600).`);
|
|
54
|
+
}
|
|
55
|
+
if (!process.stdin.isTTY) {
|
|
56
|
+
throw new AuthError("Linear API key not found and stdin is not a TTY; cannot prompt. Set LINEAR_API_KEY env var.");
|
|
57
|
+
}
|
|
58
|
+
process.stdout.write("Linear API key not found.\nGet one at https://linear.app/settings/api\nPaste it here: ");
|
|
59
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
60
|
+
let response;
|
|
61
|
+
try {
|
|
62
|
+
response = (await rl.question("")).trim();
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
rl.close();
|
|
66
|
+
}
|
|
67
|
+
const validated = validateKey(response);
|
|
68
|
+
saveKeyToEnvFile(validated, envFile);
|
|
69
|
+
process.stdout.write(`Saved to ${envFile} (mode 0600).\n`);
|
|
70
|
+
return validated;
|
|
71
|
+
}
|
|
72
|
+
// Exported for testing.
|
|
73
|
+
export const _internal = { validateKey, readKeyFromEnvFile, saveKeyToEnvFile, DEFAULT_ENV_FILE };
|
|
74
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/client/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { LinearClient } from "@linear/sdk";
|
|
2
|
+
import { type GetApiKeyOptions } from "./auth.js";
|
|
3
|
+
export declare function getLinearClient(opts?: GetApiKeyOptions): Promise<LinearClient>;
|
|
4
|
+
export declare function resetLinearClient(): void;
|
|
5
|
+
//# sourceMappingURL=linear-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-client.d.ts","sourceRoot":"","sources":["../../src/client/linear-client.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,KAAK,gBAAgB,EAAa,MAAM,WAAW,CAAC;AAI7D,wBAAsB,eAAe,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAKpF;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Thin singleton wrapper around @linear/sdk's LinearClient.
|
|
2
|
+
//
|
|
3
|
+
// Process-wide cache so a single CLI invocation only auths + constructs once,
|
|
4
|
+
// no matter how many commands invoke it. Tests can reset via resetLinearClient.
|
|
5
|
+
import { LinearClient } from "@linear/sdk";
|
|
6
|
+
import { getApiKey } from "./auth.js";
|
|
7
|
+
let cached = null;
|
|
8
|
+
export async function getLinearClient(opts) {
|
|
9
|
+
if (cached)
|
|
10
|
+
return cached;
|
|
11
|
+
const apiKey = await getApiKey(opts);
|
|
12
|
+
cached = new LinearClient({ apiKey });
|
|
13
|
+
return cached;
|
|
14
|
+
}
|
|
15
|
+
export function resetLinearClient() {
|
|
16
|
+
cached = null;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=linear-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-client.js","sourceRoot":"","sources":["../../src/client/linear-client.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAEhF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAyB,SAAS,EAAE,MAAM,WAAW,CAAC;AAE7D,IAAI,MAAM,GAAwB,IAAI,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAuB;IAC5D,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,GAAG,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,UAAU,iBAAiB;IAChC,MAAM,GAAG,IAAI,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { OutputMode } from "../output/index.js";
|
|
2
|
+
import { type SearchOptions } from "./search.js";
|
|
3
|
+
export interface BulkOptions extends Omit<SearchOptions, "limit" | "output"> {
|
|
4
|
+
setState?: string;
|
|
5
|
+
addComment?: string;
|
|
6
|
+
limit: number;
|
|
7
|
+
yes: boolean;
|
|
8
|
+
output: OutputMode;
|
|
9
|
+
}
|
|
10
|
+
export interface BulkActionPlan {
|
|
11
|
+
issueIdentifier: string;
|
|
12
|
+
issueTitle: string;
|
|
13
|
+
currentState?: string;
|
|
14
|
+
changes: {
|
|
15
|
+
stateChange?: {
|
|
16
|
+
from: string;
|
|
17
|
+
to: string;
|
|
18
|
+
};
|
|
19
|
+
commentAdded?: string;
|
|
20
|
+
};
|
|
21
|
+
skipped?: {
|
|
22
|
+
reason: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface BulkPlan {
|
|
26
|
+
actions: BulkActionPlan[];
|
|
27
|
+
totalMatched: number;
|
|
28
|
+
dryRun: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Pure: given options + matched issues, compute the plan of changes. */
|
|
31
|
+
export declare function buildBulkPlan(opts: BulkOptions, issues: Array<{
|
|
32
|
+
identifier: string;
|
|
33
|
+
title: string;
|
|
34
|
+
state?: string;
|
|
35
|
+
}>): BulkPlan;
|
|
36
|
+
/** Pure: format a plan for the user. */
|
|
37
|
+
export declare function formatBulkPlan(plan: BulkPlan, mode: OutputMode): string;
|
|
38
|
+
export declare function runBulk(opts: BulkOptions): Promise<void>;
|
|
39
|
+
//# sourceMappingURL=bulk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulk.d.ts","sourceRoot":"","sources":["../../src/commands/bulk.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAiC,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEhF,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,aAAa,EAAE,OAAO,GAAG,QAAQ,CAAC;IAE3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE;QACR,WAAW,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,OAAO,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7B;AAED,MAAM,WAAW,QAAQ;IACxB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAC5B,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAClE,QAAQ,CAwBV;AAED,wCAAwC;AACxC,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAevE;AAUD,wBAAsB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAkC9D"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// `elnora-linear bulk` — apply the same change to many Linear issues at once.
|
|
2
|
+
//
|
|
3
|
+
// Safety design:
|
|
4
|
+
// - Default behaviour is dry-run. The plan is printed; nothing is mutated.
|
|
5
|
+
// - `--yes` is required to commit mutations.
|
|
6
|
+
// - At least one of `--set-state` or `--add-comment` must be specified;
|
|
7
|
+
// otherwise the command errors out early.
|
|
8
|
+
import { getLinearClient } from "../client/index.js";
|
|
9
|
+
import { buildFilter } from "./search.js";
|
|
10
|
+
/** Pure: given options + matched issues, compute the plan of changes. */
|
|
11
|
+
export function buildBulkPlan(opts, issues) {
|
|
12
|
+
if (!opts.setState && !opts.addComment) {
|
|
13
|
+
throw new Error("bulk requires at least one of --set-state or --add-comment.");
|
|
14
|
+
}
|
|
15
|
+
const actions = issues.map((issue) => {
|
|
16
|
+
const action = {
|
|
17
|
+
issueIdentifier: issue.identifier,
|
|
18
|
+
issueTitle: issue.title,
|
|
19
|
+
currentState: issue.state,
|
|
20
|
+
changes: {},
|
|
21
|
+
};
|
|
22
|
+
if (opts.setState) {
|
|
23
|
+
if (issue.state && issue.state.toLowerCase() === opts.setState.toLowerCase()) {
|
|
24
|
+
action.skipped = { reason: `already in state "${issue.state}"` };
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
action.changes.stateChange = { from: issue.state ?? "?", to: opts.setState };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (opts.addComment) {
|
|
31
|
+
action.changes.commentAdded = opts.addComment;
|
|
32
|
+
}
|
|
33
|
+
return action;
|
|
34
|
+
});
|
|
35
|
+
return { actions, totalMatched: issues.length, dryRun: !opts.yes };
|
|
36
|
+
}
|
|
37
|
+
/** Pure: format a plan for the user. */
|
|
38
|
+
export function formatBulkPlan(plan, mode) {
|
|
39
|
+
if (mode === "json")
|
|
40
|
+
return JSON.stringify(plan, null, 2);
|
|
41
|
+
const willMutate = plan.actions.filter((a) => !a.skipped).length;
|
|
42
|
+
const header = plan.dryRun
|
|
43
|
+
? `DRY RUN — ${willMutate} of ${plan.totalMatched} matched issues would change (pass --yes to commit)`
|
|
44
|
+
: `Applied changes to ${willMutate} of ${plan.totalMatched} matched issues`;
|
|
45
|
+
if (plan.actions.length === 0)
|
|
46
|
+
return `${header}\n (no matching issues)`;
|
|
47
|
+
const lines = plan.actions.map((a) => {
|
|
48
|
+
if (a.skipped)
|
|
49
|
+
return ` ${a.issueIdentifier} SKIPPED — ${a.skipped.reason} ${a.issueTitle}`;
|
|
50
|
+
const parts = [];
|
|
51
|
+
if (a.changes.stateChange)
|
|
52
|
+
parts.push(`state: ${a.changes.stateChange.from} → ${a.changes.stateChange.to}`);
|
|
53
|
+
if (a.changes.commentAdded)
|
|
54
|
+
parts.push("+ comment");
|
|
55
|
+
return ` ${a.issueIdentifier} ${parts.join(" | ")} ${a.issueTitle}`;
|
|
56
|
+
});
|
|
57
|
+
return [header, ...lines].join("\n");
|
|
58
|
+
}
|
|
59
|
+
async function findStateIdByName(client, teamId, name) {
|
|
60
|
+
const team = await client.team(teamId);
|
|
61
|
+
const states = await team.states();
|
|
62
|
+
const match = states.nodes.find((s) => s.name.toLowerCase() === name.toLowerCase());
|
|
63
|
+
if (!match)
|
|
64
|
+
throw new Error(`No workflow state named "${name}" in team ${team.key}.`);
|
|
65
|
+
return match.id;
|
|
66
|
+
}
|
|
67
|
+
export async function runBulk(opts) {
|
|
68
|
+
const client = await getLinearClient({ allowPrompt: true });
|
|
69
|
+
const filter = await buildFilter({ ...opts, limit: opts.limit, output: opts.output }, client);
|
|
70
|
+
const issuesResult = await client.issues({ filter, first: opts.limit });
|
|
71
|
+
const fetched = issuesResult.nodes;
|
|
72
|
+
const projected = await Promise.all(fetched.map(async (issue) => {
|
|
73
|
+
const state = await issue.state;
|
|
74
|
+
return { identifier: issue.identifier, title: issue.title, state: state?.name };
|
|
75
|
+
}));
|
|
76
|
+
const plan = buildBulkPlan(opts, projected);
|
|
77
|
+
process.stdout.write(`${formatBulkPlan(plan, opts.output)}\n`);
|
|
78
|
+
if (plan.dryRun)
|
|
79
|
+
return;
|
|
80
|
+
// Apply mutations.
|
|
81
|
+
const indexByIdentifier = new Map(fetched.map((i) => [i.identifier, i]));
|
|
82
|
+
for (const action of plan.actions) {
|
|
83
|
+
if (action.skipped)
|
|
84
|
+
continue;
|
|
85
|
+
const issue = indexByIdentifier.get(action.issueIdentifier);
|
|
86
|
+
if (!issue)
|
|
87
|
+
continue;
|
|
88
|
+
const updates = {};
|
|
89
|
+
if (action.changes.stateChange) {
|
|
90
|
+
const team = await issue.team;
|
|
91
|
+
if (!team)
|
|
92
|
+
throw new Error(`Issue ${issue.identifier} has no team`);
|
|
93
|
+
updates.stateId = await findStateIdByName(client, team.id, action.changes.stateChange.to);
|
|
94
|
+
}
|
|
95
|
+
if (Object.keys(updates).length > 0) {
|
|
96
|
+
await issue.update(updates);
|
|
97
|
+
}
|
|
98
|
+
if (action.changes.commentAdded) {
|
|
99
|
+
await client.createComment({ issueId: issue.id, body: action.changes.commentAdded });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=bulk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulk.js","sourceRoot":"","sources":["../../src/commands/bulk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { OutputMode } from "../output/index.js";
|
|
2
|
+
export type CleanupAction = "close" | "cancel" | "comment";
|
|
3
|
+
export interface CleanupOptions {
|
|
4
|
+
team?: string;
|
|
5
|
+
/** Comma-separated state names; default ["Todo","Backlog"]. */
|
|
6
|
+
states?: string[];
|
|
7
|
+
inactiveDays: number;
|
|
8
|
+
action: CleanupAction;
|
|
9
|
+
message?: string;
|
|
10
|
+
limit: number;
|
|
11
|
+
yes: boolean;
|
|
12
|
+
output: OutputMode;
|
|
13
|
+
}
|
|
14
|
+
export interface CleanupActionPlan {
|
|
15
|
+
issueIdentifier: string;
|
|
16
|
+
issueTitle: string;
|
|
17
|
+
currentState?: string;
|
|
18
|
+
daysInactive: number;
|
|
19
|
+
proposed: {
|
|
20
|
+
setStateType?: "completed" | "canceled";
|
|
21
|
+
addComment?: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface CleanupPlan {
|
|
25
|
+
actions: CleanupActionPlan[];
|
|
26
|
+
totalConsidered: number;
|
|
27
|
+
dryRun: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** Pure: given options and the candidate issues, compute the plan. */
|
|
30
|
+
export declare function buildCleanupPlan(opts: CleanupOptions, issues: Array<{
|
|
31
|
+
identifier: string;
|
|
32
|
+
title: string;
|
|
33
|
+
state?: string;
|
|
34
|
+
updatedAt: Date;
|
|
35
|
+
}>, now?: Date): CleanupPlan;
|
|
36
|
+
/** Pure: format a plan for the user. */
|
|
37
|
+
export declare function formatCleanupPlan(plan: CleanupPlan, mode: OutputMode): string;
|
|
38
|
+
export declare function runCleanup(opts: CleanupOptions): Promise<void>;
|
|
39
|
+
//# sourceMappingURL=cleanup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/commands/cleanup.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE;QACT,YAAY,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC;QACxC,UAAU,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACF;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC,EACrF,GAAG,GAAE,IAAiB,GACpB,WAAW,CAqBb;AAED,wCAAwC;AACxC,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAa7E;AAUD,wBAAsB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCpE"}
|