superacli 1.1.23 → 1.1.26
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/.beads/.br_history/{issues.20260427_104638_898729442.jsonl → issues.20260428_172005_240631447.jsonl} +102 -102
- package/.beads/.br_history/{issues.20260427_104638_209211116.jsonl → issues.20260430_073641_144807989.jsonl} +103 -102
- package/.beads/.br_history/{issues.20260427_104637_752744777.jsonl → issues.20260430_073642_807075699.jsonl} +104 -102
- package/.beads/.br_history/{issues.20260427_104615_669652069.jsonl → issues.20260430_073645_625754223.jsonl} +105 -102
- package/.beads/.br_history/issues.20260430_090639_154646058.jsonl +265 -0
- package/.beads/.br_history/issues.20260430_090640_350560318.jsonl +266 -0
- package/.beads/.br_history/issues.20260430_090641_509319001.jsonl +267 -0
- package/.beads/.br_history/issues.20260430_095140_396749398.jsonl +268 -0
- package/.beads/.br_history/issues.20260501_230041_334056077.jsonl +269 -0
- package/.beads/issues.jsonl +9 -0
- package/graphify-out/GRAPH_REPORT.md +158 -138
- package/graphify-out/cache/036077d325d5aa25fc6ad7f01ca32721b02e3f981231f67fb22f59380063572b.json +1 -0
- package/graphify-out/cache/5262fb95a8855fc90efbfff767eb3aacca82c4da5799605a969554580a255404.json +1 -0
- package/graphify-out/cache/68e2b11c95ede4f05b88d001040008cb3ac72d7bd27f351497c87e128fb3b221.json +1 -0
- package/graphify-out/cache/8308aa47b396b929d5b896051ca91422afdde4861108d6f18f5094901093df79.json +1 -0
- package/graphify-out/cache/9832101a7622924f58b9e7d7752b41518049447171f7955e4c50dfd386574e12.json +1 -0
- package/graphify-out/cache/b849fb88344c2f88f3d22e42b67516e071c7d1e65f77f4ca23f25af6edfca3b3.json +1 -0
- package/graphify-out/cache/f9ee11f4395cb41b6f6900079cb7e94297c89b1b0b4798398d2f7a2e244c683b.json +1 -0
- package/graphify-out/graph.json +705 -678
- package/package.json +1 -1
- package/plugins/bluebuild/install-guidance.json +11 -0
- package/plugins/bluebuild/meta.json +5 -0
- package/plugins/bluebuild/plugin.json +99 -0
- package/plugins/bluebuild/skills/quickstart/SKILL.md +47 -0
- package/plugins/cargo-public-api/install-guidance.json +11 -0
- package/plugins/cargo-public-api/meta.json +5 -0
- package/plugins/cargo-public-api/plugin.json +88 -0
- package/plugins/cargo-public-api/skills/quickstart/SKILL.md +51 -0
- package/plugins/csview/install-guidance.json +11 -0
- package/plugins/csview/meta.json +5 -0
- package/plugins/csview/plugin.json +67 -0
- package/plugins/csview/skills/quickstart/SKILL.md +51 -0
- package/plugins/cymbal/install-guidance.json +11 -0
- package/plugins/cymbal/meta.json +5 -0
- package/plugins/cymbal/plugin.json +121 -0
- package/plugins/cymbal/skills/quickstart/SKILL.md +54 -0
- package/plugins/difftastic/install-guidance.json +11 -0
- package/plugins/difftastic/meta.json +5 -0
- package/plugins/difftastic/plugin.json +87 -0
- package/plugins/difftastic/skills/quickstart/SKILL.md +53 -0
- package/plugins/dlm/install-guidance.json +11 -0
- package/plugins/dlm/meta.json +5 -0
- package/plugins/dlm/plugin.json +85 -0
- package/plugins/dlm/skills/quickstart/SKILL.md +49 -0
- package/plugins/dofigen/install-guidance.json +11 -0
- package/plugins/dofigen/meta.json +5 -0
- package/plugins/dofigen/plugin.json +84 -0
- package/plugins/dofigen/skills/quickstart/SKILL.md +50 -0
- package/plugins/http-nu/install-guidance.json +11 -0
- package/plugins/http-nu/meta.json +5 -0
- package/plugins/http-nu/plugin.json +84 -0
- package/plugins/http-nu/skills/quickstart/SKILL.md +52 -0
- package/plugins/hwatch/install-guidance.json +11 -0
- package/plugins/hwatch/meta.json +5 -0
- package/plugins/hwatch/plugin.json +100 -0
- package/plugins/hwatch/skills/quickstart/SKILL.md +54 -0
- package/plugins/hyperfine/install-guidance.json +3 -3
- package/plugins/hyperfine/meta.json +2 -2
- package/plugins/hyperfine/plugin.json +39 -16
- package/plugins/hyperfine/skills/quickstart/SKILL.md +33 -61
- package/plugins/json5/install-guidance.json +12 -0
- package/plugins/json5/meta.json +5 -0
- package/plugins/json5/plugin.json +88 -0
- package/plugins/json5/skills/quickstart/SKILL.md +55 -0
- package/plugins/mdsf/install-guidance.json +11 -0
- package/plugins/mdsf/meta.json +5 -0
- package/plugins/mdsf/plugin.json +101 -0
- package/plugins/mdsf/skills/quickstart/SKILL.md +55 -0
- package/plugins/oha/install-guidance.json +11 -0
- package/plugins/oha/meta.json +5 -0
- package/plugins/oha/plugin.json +67 -0
- package/plugins/oha/skills/quickstart/SKILL.md +44 -0
- package/plugins/pv-migrate/install-guidance.json +11 -0
- package/plugins/pv-migrate/meta.json +5 -0
- package/plugins/pv-migrate/plugin.json +102 -0
- package/plugins/pv-migrate/skills/quickstart/SKILL.md +58 -0
- package/plugins/rash/install-guidance.json +11 -0
- package/plugins/rash/meta.json +5 -0
- package/plugins/rash/plugin.json +85 -0
- package/plugins/rash/skills/quickstart/SKILL.md +46 -0
- package/plugins/rsonpath/install-guidance.json +11 -0
- package/plugins/rsonpath/meta.json +5 -0
- package/plugins/rsonpath/plugin.json +87 -0
- package/plugins/rsonpath/skills/quickstart/SKILL.md +44 -0
- package/plugins/rsql/install-guidance.json +11 -0
- package/plugins/rsql/meta.json +5 -0
- package/plugins/rsql/plugin.json +84 -0
- package/plugins/rsql/skills/quickstart/SKILL.md +52 -0
- package/plugins/temporal/install-guidance.json +11 -0
- package/plugins/temporal/meta.json +5 -0
- package/plugins/temporal/plugin.json +95 -0
- package/plugins/temporal/skills/quickstart/SKILL.md +49 -0
- package/plugins/tkn/install-guidance.json +11 -0
- package/plugins/tkn/meta.json +5 -0
- package/plugins/tkn/plugin.json +98 -0
- package/plugins/tkn/skills/quickstart/SKILL.md +50 -0
- package/plugins/toml2json/install-guidance.json +11 -0
- package/plugins/toml2json/meta.json +5 -0
- package/plugins/toml2json/plugin.json +85 -0
- package/plugins/toml2json/skills/quickstart/SKILL.md +47 -0
- package/plugins/worktrunk/install-guidance.json +11 -0
- package/plugins/worktrunk/meta.json +5 -0
- package/plugins/worktrunk/plugin.json +116 -0
- package/plugins/worktrunk/skills/quickstart/SKILL.md +62 -0
- package/server/app.js +5 -4
- package/server/middleware/auth.js +4 -4
- package/server/routes/clients.js +46 -0
- package/server/routes/plugins.js +0 -116
- package/server/routes/settings.js +93 -0
- package/server/views/api-keys.ejs +147 -0
- package/server/views/layout.ejs +37 -0
- package/server/views/partials/head.ejs +3 -1
- package/server/views/plugins.ejs +1 -80
- package/server/views/settings.ejs +99 -0
- package/.beads/.br_history/issues.20260427_104606_658098709.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104607_085798670.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104607_627598880.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104608_071298223.jsonl +0 -261
- package/.beads/.br_history/issues.20260427_104615_198124329.jsonl +0 -261
- /package/.beads/.br_history/{issues.20260427_104606_658098709.jsonl.meta.json → issues.20260428_172005_240631447.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104607_085798670.jsonl.meta.json → issues.20260430_073641_144807989.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104607_627598880.jsonl.meta.json → issues.20260430_073642_807075699.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104608_071298223.jsonl.meta.json → issues.20260430_073645_625754223.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104615_198124329.jsonl.meta.json → issues.20260430_090639_154646058.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104615_669652069.jsonl.meta.json → issues.20260430_090640_350560318.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104637_752744777.jsonl.meta.json → issues.20260430_090641_509319001.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104638_209211116.jsonl.meta.json → issues.20260430_095140_396749398.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260427_104638_898729442.jsonl.meta.json → issues.20260501_230041_334056077.jsonl.meta.json} +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "toml2json",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Convert TOML to JSON from stdin or files",
|
|
5
|
+
"source": "https://github.com/woodruffw/toml2json",
|
|
6
|
+
"checks": [
|
|
7
|
+
{ "type": "binary", "name": "toml2json" }
|
|
8
|
+
],
|
|
9
|
+
"install_guidance": {
|
|
10
|
+
"plugin": "toml2json",
|
|
11
|
+
"binary": "toml2json",
|
|
12
|
+
"check": "which toml2json",
|
|
13
|
+
"install_steps": [
|
|
14
|
+
"cargo install toml2json",
|
|
15
|
+
"Verify: toml2json --version",
|
|
16
|
+
"supercli plugins install ./plugins/toml2json --on-conflict replace --json"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"learn": {
|
|
20
|
+
"file": "skills/quickstart/SKILL.md"
|
|
21
|
+
},
|
|
22
|
+
"commands": [
|
|
23
|
+
{
|
|
24
|
+
"namespace": "toml2json",
|
|
25
|
+
"resource": "self",
|
|
26
|
+
"action": "version",
|
|
27
|
+
"description": "Print toml2json version",
|
|
28
|
+
"adapter": "process",
|
|
29
|
+
"adapterConfig": {
|
|
30
|
+
"command": "toml2json",
|
|
31
|
+
"baseArgs": ["--version"],
|
|
32
|
+
"missingDependencyHelp": "Install toml2json: cargo install toml2json"
|
|
33
|
+
},
|
|
34
|
+
"args": []
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"namespace": "toml2json",
|
|
38
|
+
"resource": "convert",
|
|
39
|
+
"action": "file",
|
|
40
|
+
"description": "Convert a TOML file to JSON",
|
|
41
|
+
"adapter": "process",
|
|
42
|
+
"adapterConfig": {
|
|
43
|
+
"command": "toml2json",
|
|
44
|
+
"positionalArgs": ["file"],
|
|
45
|
+
"passthrough": true,
|
|
46
|
+
"timeout_ms": 30000,
|
|
47
|
+
"missingDependencyHelp": "Install toml2json"
|
|
48
|
+
},
|
|
49
|
+
"args": [
|
|
50
|
+
{ "name": "file", "type": "string", "required": true, "description": "Path to TOML file" }
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"namespace": "toml2json",
|
|
55
|
+
"resource": "convert",
|
|
56
|
+
"action": "pretty",
|
|
57
|
+
"description": "Convert TOML to pretty-printed JSON",
|
|
58
|
+
"adapter": "process",
|
|
59
|
+
"adapterConfig": {
|
|
60
|
+
"command": "toml2json",
|
|
61
|
+
"baseArgs": ["--pretty"],
|
|
62
|
+
"positionalArgs": ["file"],
|
|
63
|
+
"passthrough": true,
|
|
64
|
+
"timeout_ms": 30000,
|
|
65
|
+
"missingDependencyHelp": "Install toml2json"
|
|
66
|
+
},
|
|
67
|
+
"args": [
|
|
68
|
+
{ "name": "file", "type": "string", "required": false, "description": "Path to TOML file (reads stdin if omitted)" }
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"namespace": "toml2json",
|
|
73
|
+
"resource": "_",
|
|
74
|
+
"action": "_",
|
|
75
|
+
"description": "Passthrough to toml2json CLI",
|
|
76
|
+
"adapter": "process",
|
|
77
|
+
"adapterConfig": {
|
|
78
|
+
"command": "toml2json",
|
|
79
|
+
"passthrough": true,
|
|
80
|
+
"missingDependencyHelp": "Install toml2json"
|
|
81
|
+
},
|
|
82
|
+
"args": []
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: toml2json
|
|
3
|
+
description: Use this skill when the user wants to convert TOML files to JSON format, or pipe TOML data through stdin to get JSON output.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# toml2json Plugin
|
|
7
|
+
|
|
8
|
+
Very small CLI for converting TOML to JSON.
|
|
9
|
+
|
|
10
|
+
## Commands
|
|
11
|
+
|
|
12
|
+
### Convert
|
|
13
|
+
- `toml2json convert file` — Convert a TOML file to JSON
|
|
14
|
+
- `toml2json convert pretty` — Convert TOML to pretty-printed JSON
|
|
15
|
+
|
|
16
|
+
## Usage Examples
|
|
17
|
+
- "Convert a TOML file to JSON"
|
|
18
|
+
- "Pretty-print a TOML configuration as JSON"
|
|
19
|
+
- "Pipe TOML data and convert it"
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cargo install toml2json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Convert a file
|
|
31
|
+
toml2json config.toml
|
|
32
|
+
|
|
33
|
+
# Pretty-print
|
|
34
|
+
toml2json --pretty config.toml
|
|
35
|
+
|
|
36
|
+
# Pipe from stdin
|
|
37
|
+
cat config.toml | toml2json
|
|
38
|
+
|
|
39
|
+
# Use with jq
|
|
40
|
+
toml2json config.toml | jq '.key'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Key Features
|
|
44
|
+
- Convert TOML to JSON
|
|
45
|
+
- Read from files or stdin
|
|
46
|
+
- Pretty-print output option
|
|
47
|
+
- Pipe-friendly for shell workflows
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"plugin": "worktrunk",
|
|
3
|
+
"binary": "wt",
|
|
4
|
+
"check": "which wt",
|
|
5
|
+
"install_steps": [
|
|
6
|
+
"cargo install worktrunk",
|
|
7
|
+
"wt config shell install",
|
|
8
|
+
"supercli plugins install ./plugins/worktrunk --on-conflict replace --json"
|
|
9
|
+
],
|
|
10
|
+
"note": "Also available via: brew, pacman, winget, conda. Binary name is 'wt'. Shell integration required for cd commands."
|
|
11
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "worktrunk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for Git worktree management, designed for parallel AI agent workflows",
|
|
5
|
+
"source": "https://github.com/max-sixty/worktrunk",
|
|
6
|
+
"checks": [
|
|
7
|
+
{ "type": "binary", "name": "wt" }
|
|
8
|
+
],
|
|
9
|
+
"install_guidance": {
|
|
10
|
+
"plugin": "worktrunk",
|
|
11
|
+
"binary": "wt",
|
|
12
|
+
"check": "which wt",
|
|
13
|
+
"install_steps": [
|
|
14
|
+
"cargo install worktrunk",
|
|
15
|
+
"wt config shell install",
|
|
16
|
+
"supercli plugins install ./plugins/worktrunk --on-conflict replace --json"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"learn": {
|
|
20
|
+
"file": "skills/quickstart/SKILL.md"
|
|
21
|
+
},
|
|
22
|
+
"commands": [
|
|
23
|
+
{
|
|
24
|
+
"namespace": "worktrunk",
|
|
25
|
+
"resource": "self",
|
|
26
|
+
"action": "version",
|
|
27
|
+
"description": "Print worktrunk version",
|
|
28
|
+
"adapter": "process",
|
|
29
|
+
"adapterConfig": {
|
|
30
|
+
"command": "wt",
|
|
31
|
+
"baseArgs": ["--version"],
|
|
32
|
+
"missingDependencyHelp": "Install worktrunk: cargo install worktrunk"
|
|
33
|
+
},
|
|
34
|
+
"args": []
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"namespace": "worktrunk",
|
|
38
|
+
"resource": "branch",
|
|
39
|
+
"action": "switch",
|
|
40
|
+
"description": "Switch to a worktree branch",
|
|
41
|
+
"adapter": "process",
|
|
42
|
+
"adapterConfig": {
|
|
43
|
+
"command": "wt",
|
|
44
|
+
"baseArgs": ["switch"],
|
|
45
|
+
"passthrough": true,
|
|
46
|
+
"timeout_ms": 30000,
|
|
47
|
+
"missingDependencyHelp": "Install worktrunk"
|
|
48
|
+
},
|
|
49
|
+
"args": [
|
|
50
|
+
{ "name": "branch", "type": "string", "required": true, "description": "Branch name to switch to" }
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"namespace": "worktrunk",
|
|
55
|
+
"resource": "branch",
|
|
56
|
+
"action": "create",
|
|
57
|
+
"description": "Create and switch to a new worktree branch",
|
|
58
|
+
"adapter": "process",
|
|
59
|
+
"adapterConfig": {
|
|
60
|
+
"command": "wt",
|
|
61
|
+
"baseArgs": ["switch", "-c"],
|
|
62
|
+
"passthrough": true,
|
|
63
|
+
"timeout_ms": 30000,
|
|
64
|
+
"missingDependencyHelp": "Install worktrunk"
|
|
65
|
+
},
|
|
66
|
+
"args": [
|
|
67
|
+
{ "name": "branch", "type": "string", "required": true, "description": "New branch name" }
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"namespace": "worktrunk",
|
|
72
|
+
"resource": "branch",
|
|
73
|
+
"action": "list",
|
|
74
|
+
"description": "List all worktree branches",
|
|
75
|
+
"adapter": "process",
|
|
76
|
+
"adapterConfig": {
|
|
77
|
+
"command": "wt",
|
|
78
|
+
"baseArgs": ["list"],
|
|
79
|
+
"passthrough": true,
|
|
80
|
+
"timeout_ms": 10000,
|
|
81
|
+
"missingDependencyHelp": "Install worktrunk"
|
|
82
|
+
},
|
|
83
|
+
"args": []
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"namespace": "worktrunk",
|
|
87
|
+
"resource": "branch",
|
|
88
|
+
"action": "remove",
|
|
89
|
+
"description": "Remove a worktree branch",
|
|
90
|
+
"adapter": "process",
|
|
91
|
+
"adapterConfig": {
|
|
92
|
+
"command": "wt",
|
|
93
|
+
"baseArgs": ["remove"],
|
|
94
|
+
"passthrough": true,
|
|
95
|
+
"timeout_ms": 30000,
|
|
96
|
+
"missingDependencyHelp": "Install worktrunk"
|
|
97
|
+
},
|
|
98
|
+
"args": [
|
|
99
|
+
{ "name": "branch", "type": "string", "required": false, "description": "Branch name to remove (uses current if omitted)" }
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"namespace": "worktrunk",
|
|
104
|
+
"resource": "_",
|
|
105
|
+
"action": "_",
|
|
106
|
+
"description": "Passthrough to wt CLI",
|
|
107
|
+
"adapter": "process",
|
|
108
|
+
"adapterConfig": {
|
|
109
|
+
"command": "wt",
|
|
110
|
+
"passthrough": true,
|
|
111
|
+
"missingDependencyHelp": "Install worktrunk"
|
|
112
|
+
},
|
|
113
|
+
"args": []
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: worktrunk
|
|
3
|
+
description: Use this skill when the user wants to manage Git worktrees, switch between parallel branches, or work with multiple worktrees.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# worktrunk Plugin
|
|
7
|
+
|
|
8
|
+
CLI for Git worktree management, designed for parallel AI agent workflows.
|
|
9
|
+
|
|
10
|
+
## Commands
|
|
11
|
+
|
|
12
|
+
### Branch
|
|
13
|
+
- `worktrunk branch switch` — Switch to a worktree branch
|
|
14
|
+
- `worktrunk branch create` — Create and switch to a new worktree branch
|
|
15
|
+
- `worktrunk branch list` — List all worktree branches
|
|
16
|
+
- `worktrunk branch remove` — Remove a worktree branch
|
|
17
|
+
|
|
18
|
+
## Usage Examples
|
|
19
|
+
- "Switch to a different worktree branch"
|
|
20
|
+
- "Create a new worktree for a feature branch"
|
|
21
|
+
- "List all my worktrees"
|
|
22
|
+
- "Remove a worktree I no longer need"
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
cargo install worktrunk
|
|
28
|
+
wt config shell install
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Examples
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Switch to existing branch
|
|
35
|
+
wt switch feature-branch
|
|
36
|
+
|
|
37
|
+
# Create and switch to new branch
|
|
38
|
+
wt switch -c new-feature
|
|
39
|
+
|
|
40
|
+
# Create with external tool execution
|
|
41
|
+
wt switch -c -x claude new-feature
|
|
42
|
+
|
|
43
|
+
# List all worktrees
|
|
44
|
+
wt list
|
|
45
|
+
|
|
46
|
+
# Remove current worktree
|
|
47
|
+
wt remove
|
|
48
|
+
|
|
49
|
+
# Remove specific worktree
|
|
50
|
+
wt remove old-branch
|
|
51
|
+
|
|
52
|
+
# Checkout PR
|
|
53
|
+
wt switch pr:123
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Key Features
|
|
57
|
+
- Branch-name based worktree addressing
|
|
58
|
+
- Automatic path computation
|
|
59
|
+
- Hooks for workflow automation
|
|
60
|
+
- LLM commit message generation
|
|
61
|
+
- PR checkout support
|
|
62
|
+
- Build cache sharing between worktrees
|
package/server/app.js
CHANGED
|
@@ -15,6 +15,8 @@ const jobsRouter = require("./routes/jobs");
|
|
|
15
15
|
const askRouter = require("./routes/ask");
|
|
16
16
|
const pluginsRouter = require("./routes/plugins");
|
|
17
17
|
const docsRouter = require("./routes/docs");
|
|
18
|
+
const settingsRouter = require("./routes/settings");
|
|
19
|
+
const clientsRouter = require("./routes/clients");
|
|
18
20
|
|
|
19
21
|
const PORT = process.env.PORT || 3000;
|
|
20
22
|
|
|
@@ -34,7 +36,6 @@ app.use("/docs", express.static(path.join(__dirname, "../docs")));
|
|
|
34
36
|
// API routes (with authentication)
|
|
35
37
|
app.use("/api/dashboard", requireAuth, dashboardRouter);
|
|
36
38
|
app.use("/api/adapters", requireAuth, adaptersRouter);
|
|
37
|
-
app.use("/api/config", requireAuth, configRouter);
|
|
38
39
|
app.use("/api/commands", requireAuth, commandsRouter);
|
|
39
40
|
app.use("/api/specs", requireAuth, specsRouter);
|
|
40
41
|
app.use("/api/mcp", requireAuth, mcpRouter);
|
|
@@ -43,9 +44,9 @@ app.use("/api/jobs", requireAuth, jobsRouter);
|
|
|
43
44
|
app.use("/api/ask", requireAuth, askRouter);
|
|
44
45
|
app.use("/api/plugins", pluginsRouter);
|
|
45
46
|
app.use("/api/docs", requireAuth, docsRouter);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
app.use("/api", requireAuth, configRouter);
|
|
47
|
+
app.use("/api/clients", requireAuth, clientsRouter);
|
|
48
|
+
app.use("/api", settingsRouter);
|
|
49
|
+
app.use("/api/config", requireAuth, configRouter);
|
|
49
50
|
|
|
50
51
|
app.use("/", dashboardRouter);
|
|
51
52
|
|
|
@@ -38,10 +38,9 @@ async function requireAuth(req, res, next) {
|
|
|
38
38
|
return next();
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// Allow
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (apiKeys.length === 0 && (fullPath.includes("/settings") || fullPath.includes("/api-keys"))) {
|
|
41
|
+
// Allow HTML views (browser navigation) without auth
|
|
42
|
+
// This allows users to navigate the UI without API keys
|
|
43
|
+
if (req.accepts("html") && !req.headers["x-requested-with"] && req.query.format !== "json") {
|
|
45
44
|
return next();
|
|
46
45
|
}
|
|
47
46
|
|
|
@@ -52,6 +51,7 @@ async function requireAuth(req, res, next) {
|
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
// Validate API key against stored keys
|
|
54
|
+
const apiKeys = Array.isArray(settings.api_keys) ? settings.api_keys : [];
|
|
55
55
|
const isValid = apiKeys.some(k => k.key === apiKey);
|
|
56
56
|
|
|
57
57
|
if (!isValid) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const { Router } = require("express");
|
|
2
|
+
const { getStorage } = require("../storage/adapter");
|
|
3
|
+
const { requireAuth } = require("../middleware/auth");
|
|
4
|
+
|
|
5
|
+
const router = Router();
|
|
6
|
+
|
|
7
|
+
// Helper function to handle errors
|
|
8
|
+
function handleError(res, err) {
|
|
9
|
+
console.error(err);
|
|
10
|
+
const status = err.status || 500;
|
|
11
|
+
const error = err.error || { code: 500, type: "internal_error", message: err.message || "Internal server error" };
|
|
12
|
+
res.status(status).json({ error });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// List clients endpoint
|
|
16
|
+
router.get("/", requireAuth, async (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const storage = getStorage();
|
|
19
|
+
const clientKeys = await storage.listKeys("client:");
|
|
20
|
+
const clients = [];
|
|
21
|
+
|
|
22
|
+
for (const key of clientKeys) {
|
|
23
|
+
try {
|
|
24
|
+
const client = await storage.get(key);
|
|
25
|
+
if (client && client.client_id) {
|
|
26
|
+
clients.push(client);
|
|
27
|
+
}
|
|
28
|
+
} catch (err) {
|
|
29
|
+
// Skip if client read fails
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Sort by last_seen descending
|
|
34
|
+
clients.sort((a, b) => new Date(b.last_seen) - new Date(a.last_seen));
|
|
35
|
+
|
|
36
|
+
if (req.query.format !== "json" && req.accepts("html") && !req.xhr && !req.headers["x-requested-with"]) {
|
|
37
|
+
return res.render("clients", { clients });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
res.json({ clients });
|
|
41
|
+
} catch (err) {
|
|
42
|
+
handleError(res, err);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
module.exports = router;
|
package/server/routes/plugins.js
CHANGED
|
@@ -164,42 +164,6 @@ router.get("/", requireAuth, async (req, res) => {
|
|
|
164
164
|
}
|
|
165
165
|
})
|
|
166
166
|
|
|
167
|
-
router.get("/settings", allowIfNoApiKeys, async (req, res) => {
|
|
168
|
-
try {
|
|
169
|
-
const settings = await getSettings()
|
|
170
|
-
res.json(settings)
|
|
171
|
-
} catch (err) {
|
|
172
|
-
handleError(res, err)
|
|
173
|
-
}
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
router.put("/settings", allowIfNoApiKeys, async (req, res) => {
|
|
177
|
-
try {
|
|
178
|
-
const settings = await updateSettings(req.body || {})
|
|
179
|
-
res.json({ ok: true, settings })
|
|
180
|
-
} catch (err) {
|
|
181
|
-
handleError(res, err)
|
|
182
|
-
}
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
// Bootstrapping middleware for settings and api-keys when no API keys exist
|
|
186
|
-
async function allowIfNoApiKeys(req, res, next) {
|
|
187
|
-
try {
|
|
188
|
-
const { getCachedSettings } = require("../middleware/auth")
|
|
189
|
-
const settings = await getCachedSettings()
|
|
190
|
-
const apiKeys = Array.isArray(settings.api_keys) ? settings.api_keys : []
|
|
191
|
-
if (apiKeys.length === 0) {
|
|
192
|
-
return next()
|
|
193
|
-
}
|
|
194
|
-
// If API keys exist, require auth
|
|
195
|
-
return requireAuth(req, res, next)
|
|
196
|
-
} catch (err) {
|
|
197
|
-
console.error("Bootstrapping middleware error:", err)
|
|
198
|
-
// Fail open on error
|
|
199
|
-
next()
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
167
|
router.post("/", requireAuth, async (req, res) => {
|
|
204
168
|
try {
|
|
205
169
|
const sourceType = String((req.body && req.body.source_type) || "json").trim().toLowerCase()
|
|
@@ -225,37 +189,6 @@ router.post("/upload", requireAuth, async (req, res) => {
|
|
|
225
189
|
}
|
|
226
190
|
})
|
|
227
191
|
|
|
228
|
-
// List clients endpoint (must be before /:name)
|
|
229
|
-
router.get("/clients", requireAuth, async (req, res) => {
|
|
230
|
-
try {
|
|
231
|
-
const storage = getStorage()
|
|
232
|
-
const clientKeys = await storage.listKeys("client:")
|
|
233
|
-
const clients = []
|
|
234
|
-
|
|
235
|
-
for (const key of clientKeys) {
|
|
236
|
-
try {
|
|
237
|
-
const client = await storage.get(key)
|
|
238
|
-
if (client && client.client_id) {
|
|
239
|
-
clients.push(client)
|
|
240
|
-
}
|
|
241
|
-
} catch (err) {
|
|
242
|
-
// Skip if client read fails
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Sort by last_seen descending
|
|
247
|
-
clients.sort((a, b) => new Date(b.last_seen) - new Date(a.last_seen))
|
|
248
|
-
|
|
249
|
-
if (req.query.format !== "json" && req.accepts("html") && !req.xhr && !req.headers["x-requested-with"]) {
|
|
250
|
-
return res.render("clients", { clients })
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
res.json({ clients })
|
|
254
|
-
} catch (err) {
|
|
255
|
-
handleError(res, err)
|
|
256
|
-
}
|
|
257
|
-
})
|
|
258
|
-
|
|
259
192
|
router.get("/:name", requireAuth, async (req, res) => {
|
|
260
193
|
try {
|
|
261
194
|
const plugin = await getServerPlugin(req.params.name)
|
|
@@ -409,53 +342,4 @@ router.get("/clients", async (req, res) => {
|
|
|
409
342
|
}
|
|
410
343
|
})
|
|
411
344
|
|
|
412
|
-
// API Keys endpoints
|
|
413
|
-
|
|
414
|
-
// POST /api/plugins/api-keys - Create API key
|
|
415
|
-
router.post("/api-keys", allowIfNoApiKeys, async (req, res) => {
|
|
416
|
-
try {
|
|
417
|
-
const { name } = req.body || {}
|
|
418
|
-
if (!name || typeof name !== "string") {
|
|
419
|
-
return res.status(400).json({ error: "name is required", type: "invalid_argument" })
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
const settings = await getSettings()
|
|
423
|
-
const crypto = require("crypto")
|
|
424
|
-
const key = crypto.randomBytes(32).toString("hex")
|
|
425
|
-
|
|
426
|
-
const newApiKey = {
|
|
427
|
-
name: String(name).trim(),
|
|
428
|
-
key,
|
|
429
|
-
created_at: new Date().toISOString()
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const apiKeys = Array.isArray(settings.api_keys) ? settings.api_keys : []
|
|
433
|
-
apiKeys.push(newApiKey)
|
|
434
|
-
|
|
435
|
-
await updateSettings({ api_keys: apiKeys })
|
|
436
|
-
res.status(201).json(newApiKey)
|
|
437
|
-
} catch (err) {
|
|
438
|
-
handleError(res, err)
|
|
439
|
-
}
|
|
440
|
-
})
|
|
441
|
-
|
|
442
|
-
// DELETE /api/plugins/api-keys/:key - Delete API key
|
|
443
|
-
router.delete("/api-keys/:key", allowIfNoApiKeys, async (req, res) => {
|
|
444
|
-
try {
|
|
445
|
-
const keyToDelete = req.params.key
|
|
446
|
-
const settings = await getSettings()
|
|
447
|
-
const apiKeys = Array.isArray(settings.api_keys) ? settings.api_keys : []
|
|
448
|
-
const filtered = apiKeys.filter(k => k.key !== keyToDelete)
|
|
449
|
-
|
|
450
|
-
if (filtered.length === apiKeys.length) {
|
|
451
|
-
return res.status(404).json({ error: "API key not found", type: "resource_not_found" })
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
await updateSettings({ api_keys: filtered })
|
|
455
|
-
res.json({ ok: true })
|
|
456
|
-
} catch (err) {
|
|
457
|
-
handleError(res, err)
|
|
458
|
-
}
|
|
459
|
-
})
|
|
460
|
-
|
|
461
345
|
module.exports = router
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const { Router } = require("express");
|
|
2
|
+
const { getSettings, updateSettings } = require("../services/pluginsService");
|
|
3
|
+
|
|
4
|
+
const router = Router();
|
|
5
|
+
|
|
6
|
+
// Helper function to handle errors
|
|
7
|
+
function handleError(res, err) {
|
|
8
|
+
console.error(err);
|
|
9
|
+
const status = err.status || 500;
|
|
10
|
+
const error = err.error || { code: 500, type: "internal_error", message: err.message || "Internal server error" };
|
|
11
|
+
res.status(status).json({ error });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// GET /settings - Render settings view
|
|
15
|
+
router.get("/settings", async (req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const settings = await getSettings()
|
|
18
|
+
if (req.query.format !== "json" && req.accepts("html") && !req.xhr && !req.headers["x-requested-with"]) {
|
|
19
|
+
return res.render("settings", { settings })
|
|
20
|
+
}
|
|
21
|
+
res.json(settings)
|
|
22
|
+
} catch (err) {
|
|
23
|
+
handleError(res, err)
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// PUT /settings - Update settings
|
|
28
|
+
router.put("/settings", async (req, res) => {
|
|
29
|
+
try {
|
|
30
|
+
const settings = await updateSettings(req.body || {})
|
|
31
|
+
res.json({ ok: true, settings })
|
|
32
|
+
} catch (err) {
|
|
33
|
+
handleError(res, err)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// GET /api-keys - Render API keys view
|
|
38
|
+
router.get("/api-keys", async (req, res) => {
|
|
39
|
+
try {
|
|
40
|
+
const settings = await getSettings()
|
|
41
|
+
if (req.query.format !== "json" && req.accepts("html") && !req.xhr && !req.headers["x-requested-with"]) {
|
|
42
|
+
return res.render("api-keys", { settings })
|
|
43
|
+
}
|
|
44
|
+
res.json({ api_keys: settings.api_keys || [] })
|
|
45
|
+
} catch (err) {
|
|
46
|
+
handleError(res, err)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// POST /api-keys - Create API key
|
|
51
|
+
router.post("/api-keys", async (req, res) => {
|
|
52
|
+
try {
|
|
53
|
+
const { name } = req.body || {}
|
|
54
|
+
if (!name || typeof name !== "string") {
|
|
55
|
+
return res.status(400).json({ error: "name is required", type: "invalid_argument" })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const settings = await getSettings()
|
|
59
|
+
const crypto = require("crypto")
|
|
60
|
+
const key = crypto.randomBytes(32).toString("hex")
|
|
61
|
+
|
|
62
|
+
const newApiKey = {
|
|
63
|
+
name: String(name).trim(),
|
|
64
|
+
key,
|
|
65
|
+
created_at: new Date().toISOString()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const apiKeys = Array.isArray(settings.api_keys) ? settings.api_keys : []
|
|
69
|
+
apiKeys.push(newApiKey)
|
|
70
|
+
|
|
71
|
+
await updateSettings({ api_keys: apiKeys })
|
|
72
|
+
res.status(201).json(newApiKey)
|
|
73
|
+
} catch (err) {
|
|
74
|
+
handleError(res, err)
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// DELETE /api-keys/:key - Delete API key
|
|
79
|
+
router.delete("/api-keys/:key", async (req, res) => {
|
|
80
|
+
try {
|
|
81
|
+
const keyToDelete = req.params.key
|
|
82
|
+
const settings = await getSettings()
|
|
83
|
+
const apiKeys = Array.isArray(settings.api_keys) ? settings.api_keys : []
|
|
84
|
+
const filtered = apiKeys.filter(k => k.key !== keyToDelete)
|
|
85
|
+
|
|
86
|
+
await updateSettings({ api_keys: filtered })
|
|
87
|
+
res.json({ ok: true })
|
|
88
|
+
} catch (err) {
|
|
89
|
+
handleError(res, err)
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
module.exports = router;
|