@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.
Files changed (118) hide show
  1. package/.claude-plugin/marketplace.json +19 -0
  2. package/.claude-plugin/plugin.json +19 -0
  3. package/CHANGELOG.md +29 -0
  4. package/LICENSE +201 -0
  5. package/README.md +61 -0
  6. package/agents/linear-issue-creator.md +45 -0
  7. package/agents/linear-issue-reviewer.md +44 -0
  8. package/agents/linear-issue-updater.md +45 -0
  9. package/agents/linear-url-to-issues.md +51 -0
  10. package/commands/linear-bulk.md +42 -0
  11. package/commands/linear-cleanup.md +45 -0
  12. package/commands/linear-curator-run.md +39 -0
  13. package/commands/linear-my-issues.md +25 -0
  14. package/commands/linear-search.md +32 -0
  15. package/commands/linear-sync.md +54 -0
  16. package/dist/cli.d.ts +3 -0
  17. package/dist/cli.d.ts.map +1 -0
  18. package/dist/cli.js +126 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/client/auth.d.ts +21 -0
  21. package/dist/client/auth.d.ts.map +1 -0
  22. package/dist/client/auth.js +74 -0
  23. package/dist/client/auth.js.map +1 -0
  24. package/dist/client/index.d.ts +3 -0
  25. package/dist/client/index.d.ts.map +1 -0
  26. package/dist/client/index.js +3 -0
  27. package/dist/client/index.js.map +1 -0
  28. package/dist/client/linear-client.d.ts +5 -0
  29. package/dist/client/linear-client.d.ts.map +1 -0
  30. package/dist/client/linear-client.js +18 -0
  31. package/dist/client/linear-client.js.map +1 -0
  32. package/dist/commands/bulk.d.ts +39 -0
  33. package/dist/commands/bulk.d.ts.map +1 -0
  34. package/dist/commands/bulk.js +103 -0
  35. package/dist/commands/bulk.js.map +1 -0
  36. package/dist/commands/cleanup.d.ts +39 -0
  37. package/dist/commands/cleanup.d.ts.map +1 -0
  38. package/dist/commands/cleanup.js +100 -0
  39. package/dist/commands/cleanup.js.map +1 -0
  40. package/dist/commands/curator.d.ts +23 -0
  41. package/dist/commands/curator.d.ts.map +1 -0
  42. package/dist/commands/curator.js +66 -0
  43. package/dist/commands/curator.js.map +1 -0
  44. package/dist/commands/index.d.ts +7 -0
  45. package/dist/commands/index.d.ts.map +1 -0
  46. package/dist/commands/index.js +7 -0
  47. package/dist/commands/index.js.map +1 -0
  48. package/dist/commands/my-issues.d.ts +7 -0
  49. package/dist/commands/my-issues.d.ts.map +1 -0
  50. package/dist/commands/my-issues.js +24 -0
  51. package/dist/commands/my-issues.js.map +1 -0
  52. package/dist/commands/search.d.ts +20 -0
  53. package/dist/commands/search.d.ts.map +1 -0
  54. package/dist/commands/search.js +54 -0
  55. package/dist/commands/search.js.map +1 -0
  56. package/dist/commands/sync.d.ts +87 -0
  57. package/dist/commands/sync.d.ts.map +1 -0
  58. package/dist/commands/sync.js +229 -0
  59. package/dist/commands/sync.js.map +1 -0
  60. package/dist/config/index.d.ts +3 -0
  61. package/dist/config/index.d.ts.map +1 -0
  62. package/dist/config/index.js +4 -0
  63. package/dist/config/index.js.map +1 -0
  64. package/dist/config/loader.d.ts +12 -0
  65. package/dist/config/loader.d.ts.map +1 -0
  66. package/dist/config/loader.js +140 -0
  67. package/dist/config/loader.js.map +1 -0
  68. package/dist/config/types.d.ts +133 -0
  69. package/dist/config/types.d.ts.map +1 -0
  70. package/dist/config/types.js +13 -0
  71. package/dist/config/types.js.map +1 -0
  72. package/dist/output/formatter.d.ts +16 -0
  73. package/dist/output/formatter.d.ts.map +1 -0
  74. package/dist/output/formatter.js +34 -0
  75. package/dist/output/formatter.js.map +1 -0
  76. package/dist/output/index.d.ts +2 -0
  77. package/dist/output/index.d.ts.map +1 -0
  78. package/dist/output/index.js +2 -0
  79. package/dist/output/index.js.map +1 -0
  80. package/dist/signals/external-command.d.ts +30 -0
  81. package/dist/signals/external-command.d.ts.map +1 -0
  82. package/dist/signals/external-command.js +96 -0
  83. package/dist/signals/external-command.js.map +1 -0
  84. package/dist/signals/index.d.ts +4 -0
  85. package/dist/signals/index.d.ts.map +1 -0
  86. package/dist/signals/index.js +4 -0
  87. package/dist/signals/index.js.map +1 -0
  88. package/dist/signals/registry.d.ts +5 -0
  89. package/dist/signals/registry.d.ts.map +1 -0
  90. package/dist/signals/registry.js +29 -0
  91. package/dist/signals/registry.js.map +1 -0
  92. package/dist/signals/types.d.ts +25 -0
  93. package/dist/signals/types.d.ts.map +1 -0
  94. package/dist/signals/types.js +9 -0
  95. package/dist/signals/types.js.map +1 -0
  96. package/package.json +76 -0
  97. package/references/projects.example.json +26 -0
  98. package/references/projects.placeholder.json +6 -0
  99. package/references/repos.example.json +21 -0
  100. package/references/repos.placeholder.json +6 -0
  101. package/references/signal-sources.example.json +42 -0
  102. package/references/signal-sources.placeholder.json +6 -0
  103. package/references/slack.example.json +10 -0
  104. package/references/slack.placeholder.json +8 -0
  105. package/references/teams.example.json +21 -0
  106. package/references/teams.placeholder.json +6 -0
  107. package/references/users.example.json +18 -0
  108. package/references/users.placeholder.json +6 -0
  109. package/references/workflows.example.json +44 -0
  110. package/references/workflows.placeholder.json +7 -0
  111. package/schemas/projects.json +47 -0
  112. package/schemas/repos.json +41 -0
  113. package/schemas/signal-sources.json +90 -0
  114. package/schemas/slack.json +45 -0
  115. package/schemas/teams.json +43 -0
  116. package/schemas/users.json +41 -0
  117. package/schemas/workflows.json +61 -0
  118. package/skills/linear-workspace/SKILL.md +52 -0
@@ -0,0 +1,41 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/Elnora-AI/elnora-linear/blob/main/schemas/repos.json",
4
+ "title": "Repos",
5
+ "description": "GitHub repositories the curator scans for signals (commits, PRs). Populated by `linear-sync repos`.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "_placeholder": { "type": "boolean", "const": true },
11
+ "_example": { "type": "boolean", "const": true },
12
+ "_populated_by": { "type": "string" },
13
+ "repos": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "properties": {
19
+ "name": {
20
+ "type": "string",
21
+ "description": "Repository name (without org)"
22
+ },
23
+ "org": {
24
+ "type": "string",
25
+ "description": "GitHub org or user that owns the repo"
26
+ },
27
+ "local_path": {
28
+ "type": "string",
29
+ "description": "Optional local clone path for git-log scans; supports leading ~"
30
+ },
31
+ "default_branch": {
32
+ "type": "string",
33
+ "default": "main"
34
+ }
35
+ },
36
+ "required": ["name"]
37
+ }
38
+ }
39
+ },
40
+ "required": ["repos"]
41
+ }
@@ -0,0 +1,90 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/Elnora-AI/elnora-linear/blob/main/schemas/signal-sources.json",
4
+ "title": "Signal Sources",
5
+ "description": "Pluggable signal sources the curator consults when validating Linear issue state. Each source has a `type` that dispatches to a built-in collector (github_commits, github_pr, slack_messages, linear_issues) or generic extension point (external_command, mcp_tool).",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "_placeholder": { "type": "boolean", "const": true },
11
+ "_example": { "type": "boolean", "const": true },
12
+ "_populated_by": { "type": "string" },
13
+ "sources": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "object",
17
+ "required": ["type", "name"],
18
+ "properties": {
19
+ "type": {
20
+ "type": "string",
21
+ "enum": [
22
+ "github_commits",
23
+ "github_pr",
24
+ "slack_messages",
25
+ "linear_issues",
26
+ "external_command",
27
+ "mcp_tool"
28
+ ]
29
+ },
30
+ "name": {
31
+ "type": "string",
32
+ "description": "Human-readable identifier for this source in curator reports"
33
+ },
34
+ "enabled": { "type": "boolean", "default": true }
35
+ },
36
+ "allOf": [
37
+ {
38
+ "if": { "properties": { "type": { "const": "github_commits" } }, "required": ["type"] },
39
+ "then": {
40
+ "properties": {
41
+ "repos": { "type": "array", "items": { "type": "string" } },
42
+ "lookback_days": { "type": "integer", "minimum": 1, "maximum": 365 }
43
+ }
44
+ }
45
+ },
46
+ {
47
+ "if": { "properties": { "type": { "const": "github_pr" } }, "required": ["type"] },
48
+ "then": {
49
+ "properties": {
50
+ "repos": { "type": "array", "items": { "type": "string" } }
51
+ }
52
+ }
53
+ },
54
+ {
55
+ "if": { "properties": { "type": { "const": "slack_messages" } }, "required": ["type"] },
56
+ "then": {
57
+ "properties": {
58
+ "channels": { "type": "array", "items": { "type": "string" } },
59
+ "match_patterns": { "type": "array", "items": { "type": "string" } }
60
+ }
61
+ }
62
+ },
63
+ {
64
+ "if": { "properties": { "type": { "const": "external_command" } }, "required": ["type"] },
65
+ "then": {
66
+ "required": ["command"],
67
+ "properties": {
68
+ "command": { "type": "string", "minLength": 1 },
69
+ "parse_as": { "type": "string", "enum": ["json", "lines", "regex"] },
70
+ "issue_match_field": { "type": "string" }
71
+ }
72
+ }
73
+ },
74
+ {
75
+ "if": { "properties": { "type": { "const": "mcp_tool" } }, "required": ["type"] },
76
+ "then": {
77
+ "required": ["server", "tool"],
78
+ "properties": {
79
+ "server": { "type": "string" },
80
+ "tool": { "type": "string" },
81
+ "args": { "type": "object" }
82
+ }
83
+ }
84
+ }
85
+ ]
86
+ }
87
+ }
88
+ },
89
+ "required": ["sources"]
90
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/Elnora-AI/elnora-linear/blob/main/schemas/slack.json",
4
+ "title": "Slack",
5
+ "description": "Slack channels + outbound-routing allowlists used by the curator. Allowlists are an intentional security model: the curator never posts to channels or DMs not explicitly listed here.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "_placeholder": { "type": "boolean", "const": true },
11
+ "_example": { "type": "boolean", "const": true },
12
+ "_populated_by": { "type": "string" },
13
+ "channels": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "properties": {
19
+ "id": {
20
+ "type": "string",
21
+ "pattern": "^C[A-Z0-9]+$",
22
+ "description": "Slack channel ID"
23
+ },
24
+ "name": {
25
+ "type": "string",
26
+ "description": "Channel name without the leading #"
27
+ },
28
+ "purpose": { "type": "string" }
29
+ },
30
+ "required": ["id", "name"]
31
+ }
32
+ },
33
+ "allowed_channels": {
34
+ "type": "array",
35
+ "items": { "type": "string", "pattern": "^C[A-Z0-9]+$" },
36
+ "description": "Channel IDs the curator is permitted to post in. Posts to unlisted channels are silently dropped."
37
+ },
38
+ "allowed_dm_users": {
39
+ "type": "array",
40
+ "items": { "type": "string" },
41
+ "description": "User keys (resolving via users.json to slack_user_id) the curator is permitted to DM."
42
+ }
43
+ },
44
+ "required": ["channels", "allowed_channels", "allowed_dm_users"]
45
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/Elnora-AI/elnora-linear/blob/main/schemas/teams.json",
4
+ "title": "Teams",
5
+ "description": "Linear teams the plugin operates on. Populated by `linear-sync teams` from the Linear API.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "_placeholder": { "type": "boolean", "const": true },
11
+ "_example": { "type": "boolean", "const": true },
12
+ "_populated_by": { "type": "string" },
13
+ "teams": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "properties": {
19
+ "key": {
20
+ "type": "string",
21
+ "pattern": "^[A-Z]{2,8}$",
22
+ "description": "Linear team key / issue prefix, e.g. ENG"
23
+ },
24
+ "name": {
25
+ "type": "string",
26
+ "minLength": 1,
27
+ "description": "Human-readable team name from Linear"
28
+ },
29
+ "description": {
30
+ "type": "string",
31
+ "description": "Optional one-line description"
32
+ },
33
+ "default_project": {
34
+ "type": "string",
35
+ "description": "Optional default project name for new issues on this team"
36
+ }
37
+ },
38
+ "required": ["key", "name"]
39
+ }
40
+ }
41
+ },
42
+ "required": ["teams"]
43
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/Elnora-AI/elnora-linear/blob/main/schemas/users.json",
4
+ "title": "Users",
5
+ "description": "Users referenced by name across the workspace. Populated by `linear-sync users` from the Linear API; optional Slack user IDs added via `linear-sync slack`.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "_placeholder": { "type": "boolean", "const": true },
11
+ "_example": { "type": "boolean", "const": true },
12
+ "_populated_by": { "type": "string" },
13
+ "users": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "properties": {
19
+ "key": {
20
+ "type": "string",
21
+ "pattern": "^[a-z][a-z0-9_-]*$",
22
+ "description": "Short handle used across other reference files (e.g. project lead, signal recipient)"
23
+ },
24
+ "name": { "type": "string", "minLength": 1 },
25
+ "email": { "type": "string", "format": "email" },
26
+ "linear_user_id": {
27
+ "type": "string",
28
+ "description": "Linear UUID, resolved via API"
29
+ },
30
+ "slack_user_id": {
31
+ "type": "string",
32
+ "pattern": "^U[A-Z0-9]+$",
33
+ "description": "Slack user ID for direct messages (optional; only set if curator messaging is enabled)"
34
+ }
35
+ },
36
+ "required": ["key", "name"]
37
+ }
38
+ }
39
+ },
40
+ "required": ["users"]
41
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/Elnora-AI/elnora-linear/blob/main/schemas/workflows.json",
4
+ "title": "Workflows",
5
+ "description": "Linear workflow states (fetched from the API) plus user-defined curator rules. Rules are tiered: high = auto-apply, medium = ask the assignee, low = surface in summary report only.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "_placeholder": { "type": "boolean", "const": true },
11
+ "_example": { "type": "boolean", "const": true },
12
+ "_populated_by": { "type": "string" },
13
+ "states": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "properties": {
19
+ "name": { "type": "string", "minLength": 1 },
20
+ "type": {
21
+ "type": "string",
22
+ "enum": ["backlog", "unstarted", "started", "completed", "canceled", "triage"]
23
+ }
24
+ },
25
+ "required": ["name", "type"]
26
+ }
27
+ },
28
+ "rules": {
29
+ "type": "array",
30
+ "items": {
31
+ "type": "object",
32
+ "additionalProperties": false,
33
+ "required": ["id", "tier", "description", "when", "action"],
34
+ "properties": {
35
+ "id": {
36
+ "type": "string",
37
+ "pattern": "^[a-z][a-z0-9-]*$",
38
+ "description": "Stable rule identifier surfaced in audit logs"
39
+ },
40
+ "tier": {
41
+ "type": "string",
42
+ "enum": ["high", "medium", "low"],
43
+ "description": "high = auto-apply; medium = ask assignee; low = report only"
44
+ },
45
+ "description": { "type": "string", "minLength": 1 },
46
+ "when": {
47
+ "type": "object",
48
+ "description": "Match conditions; the curator evaluates these against signals and issue state",
49
+ "additionalProperties": true
50
+ },
51
+ "action": {
52
+ "type": "object",
53
+ "description": "What to do when the rule matches",
54
+ "additionalProperties": true
55
+ }
56
+ }
57
+ }
58
+ }
59
+ },
60
+ "required": ["states", "rules"]
61
+ }
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: linear-workspace
3
+ description: >
4
+ Linear issue management — routes work to specialized agents and slash commands.
5
+ Use when: creating, updating, searching, bulk-editing, cleaning up, syncing Linear.
6
+ TRIGGERS: "linear", "ticket", "issue", "create issue", "update issue", "search issues",
7
+ "my issues", "bulk", "cleanup", "sync", "log bug", "file issue", "report bug",
8
+ "new ticket", "add task", "open ticket", "open issue", "throw this in linear",
9
+ "track this in linear", "capture as issue", "put on the backlog".
10
+ ---
11
+
12
+ # Linear Workspace
13
+
14
+ Router for Linear work. Dispatches to specialized agents or slash commands rather than running things inline.
15
+
16
+ ## Dispatch table
17
+
18
+ | Intent | Action |
19
+ |---|---|
20
+ | Create one issue from a description | Agent: `linear-issue-creator` |
21
+ | Create issues from a URL / article / design | Agent: `linear-url-to-issues` |
22
+ | Review one existing issue (clarity, completeness) | Agent: `linear-issue-reviewer` |
23
+ | Edit one existing issue (state, assignee, comment, close, …) | Agent: `linear-issue-updater` |
24
+ | Search / list issues | Slash command: `/linear-search` |
25
+ | List your own issues | Slash command: `/linear-my-issues` |
26
+ | Apply the same change to many issues | Slash command: `/linear-bulk` |
27
+ | Find + handle stale issues | Slash command: `/linear-cleanup` |
28
+ | Refresh reference data from Linear | Slash command: `/linear-sync` |
29
+ | Run the curator (collect signals from external sources) | Slash command: `/linear-curator-run` |
30
+
31
+ ## First-run install
32
+
33
+ 1. Install the plugin: `/plugin marketplace add Elnora-AI/elnora-linear` then `/plugin install linear-workspace@elnora-linear`
34
+ 2. Make sure the `elnora-linear` CLI is on your PATH: `npm install -g @elnora-ai/linear`
35
+ 3. On your first Linear command, you'll be prompted for your Linear API key (get one at https://linear.app/settings/api). It's saved to `~/.config/elnora-linear/.env` (mode 0600).
36
+ 4. Populate the reference files: `/linear-sync` (runs `elnora-linear sync all` — fetches teams, projects, users, workflows in one batch).
37
+
38
+ ## Reference files
39
+
40
+ The plugin reads user-specific config from `~/.config/elnora-linear/` by default (override via `LINEAR_REFERENCES_DIR`):
41
+
42
+ - `teams.json`, `projects.json`, `users.json`, `workflows.json` — populated by `/linear-sync`
43
+ - `slack.json`, `repos.json`, `signal-sources.json` — populated manually in v0; interactive prompts coming in a future release
44
+
45
+ Run `elnora-linear sync verify` to see which are populated vs placeholder.
46
+
47
+ ## Safety guardrails
48
+
49
+ - `bulk` and `cleanup` default to **dry-run**; they print what would change and require explicit `--yes` to commit
50
+ - `bulk` requires at least one of `--set-state` or `--add-comment` — refuses no-op invocations
51
+ - `cleanup` defaults to `comment` action (least destructive); `close` / `cancel` are explicit opt-ins
52
+ - `external_command` signal sources run user-configured commands with the user's privileges — only configure commands from sources the user trusts