misterpropre 0.0.1
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/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/chunk-FDTKUXXE.js +418 -0
- package/dist/chunk-HMGF6JWH.js +884 -0
- package/dist/chunk-JCB4UDCP.js +1348 -0
- package/dist/cli.js +256 -0
- package/dist/dashboard-W6QCV3NV.js +335 -0
- package/dist/setup-R2IL4RHH.js +8 -0
- package/package.json +64 -0
- package/skills/dependabot-purge/SKILL.md.tmpl +137 -0
- package/skills/dependabot-purge/references/fix-matrix.md.tmpl +106 -0
- package/skills/dependabot-purge/scripts/ensure_docker.sh +33 -0
- package/skills/dependabot-purge/scripts/wait_checks.sh +66 -0
- package/skills/security-dependabot-fix/SKILL.md.tmpl +171 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ayman Kahya
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# misterpropre
|
|
2
|
+
|
|
3
|
+
> Mr. Clean for dependencies β a bilingual (π«π·/π¬π§) CLI that automates Dependabot/Renovate
|
|
4
|
+
> security fixes and bot-PR triage across your repos, driven by an AI coding agent
|
|
5
|
+
> (Claude Code or Kiro).
|
|
6
|
+
|
|
7
|
+
`misterpropre` (`mrp`) wraps two workflows and runs them, unattended, over one repo or many:
|
|
8
|
+
|
|
9
|
+
- **Security fixes** β resolve open Dependabot security alerts and open a PR for review.
|
|
10
|
+
- **Renovate/Dependabot triage** β review the bot PRs, merge the safe ones, hand off the rest.
|
|
11
|
+
|
|
12
|
+
It runs each repo in an isolated git worktree, shows a live progress grid, and can run on a
|
|
13
|
+
native daily schedule.
|
|
14
|
+
|
|
15
|
+
> β οΈ **It acts on your behalf.** A real AI agent opens PRs and merges bot PRs in the repos you
|
|
16
|
+
> point it at. Start with a single repo and review what it does before trusting it broadly.
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- **Node.js β₯ 20**
|
|
21
|
+
- **[GitHub CLI](https://cli.github.com/)** (`gh`), authenticated (`gh auth status`)
|
|
22
|
+
- One agent provider, installed and logged in:
|
|
23
|
+
- **[Claude Code](https://claude.com/claude-code)** β `claude` on your `PATH`, or
|
|
24
|
+
- **[Kiro CLI](https://kiro.dev/)** β `kiro-cli`, logged in via `kiro-cli login`
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npm install -g misterpropre
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
mrp # interactive dashboard (first run walks you through setup)
|
|
36
|
+
mrp setup # onboarding, or edit your config inline
|
|
37
|
+
mrp doctor # check that your toolchain is ready
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
From the dashboard you can run a security pass, a Renovate/Dependabot pass, or both, on **all**
|
|
41
|
+
your configured repos or a **single** one (toggle with `Ctrl+Tab`), and schedule a daily run.
|
|
42
|
+
|
|
43
|
+
## Commands
|
|
44
|
+
|
|
45
|
+
| Command | What it does |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `mrp` | Interactive dashboard (on a TTY) |
|
|
48
|
+
| `mrp run [reposβ¦]` | Run a pass. `--phase fix\|purge\|both`, `--all`, `--provider claude\|kiro` |
|
|
49
|
+
| `mrp schedule set\|status\|clear` | Native daily schedule (launchd, macOS) |
|
|
50
|
+
| `mrp setup` | First-run onboarding, or inline config editor |
|
|
51
|
+
| `mrp login <claude\|kiro>` | Install (if needed) + log in to a provider |
|
|
52
|
+
| `mrp config show\|path\|init` | View / locate / initialise the config |
|
|
53
|
+
| `mrp config secrets list\|set\|rm` | Manage stored secrets (chmod-600, separate from config) |
|
|
54
|
+
| `mrp skills status\|install` | Manage the bundled agent skills |
|
|
55
|
+
| `mrp doctor` | Verify the local toolchain |
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
Config lives at `~/.config/misterpropre/config.json` (honours `XDG_CONFIG_HOME`) and holds your
|
|
60
|
+
language, GitHub org, where your repos are cloned, the chosen repos, the provider, and the
|
|
61
|
+
schedule. Secrets (optional tokens, notification creds) are stored separately with `600`
|
|
62
|
+
permissions. Edit everything inline with `mrp setup`.
|
|
63
|
+
|
|
64
|
+
## Scheduling
|
|
65
|
+
|
|
66
|
+
`mrp schedule set --time HH:MM` installs a daily LaunchAgent (macOS) that runs the automation
|
|
67
|
+
unattended and logs to `~/.config/misterpropre/schedule.log`. `mrp schedule clear` removes it.
|
|
68
|
+
You can also configure it from the dashboard ("Schedule daily run").
|
|
69
|
+
|
|
70
|
+
## Develop
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
npm install
|
|
74
|
+
npm run dev -- doctor # run from source
|
|
75
|
+
npm run build # bundle to dist/
|
|
76
|
+
npm test # unit tests
|
|
77
|
+
npm run typecheck # tsc --noEmit
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Built around two bundled skills (`security-dependabot-fix`, `dependabot-purge`) driven headlessly
|
|
81
|
+
through a provider adapter.
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
[MIT](./LICENSE) Β© Ayman Kahya
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Answered,
|
|
3
|
+
Banner,
|
|
4
|
+
Chips,
|
|
5
|
+
KeyVal,
|
|
6
|
+
MultiSelect,
|
|
7
|
+
Panel,
|
|
8
|
+
Select,
|
|
9
|
+
Spinner,
|
|
10
|
+
configPath,
|
|
11
|
+
defaultConfig,
|
|
12
|
+
fetchOrgRepoNames,
|
|
13
|
+
installAllSkills,
|
|
14
|
+
linkProvider,
|
|
15
|
+
readConfig,
|
|
16
|
+
realLinkDeps,
|
|
17
|
+
skillsRootFor,
|
|
18
|
+
theme,
|
|
19
|
+
wizardT,
|
|
20
|
+
writeConfig
|
|
21
|
+
} from "./chunk-HMGF6JWH.js";
|
|
22
|
+
|
|
23
|
+
// src/commands/setup.ts
|
|
24
|
+
import { createElement } from "react";
|
|
25
|
+
import { render } from "ink";
|
|
26
|
+
|
|
27
|
+
// src/ui/setup.tsx
|
|
28
|
+
import { useEffect, useState } from "react";
|
|
29
|
+
import { Box, Text, useApp } from "ink";
|
|
30
|
+
import TextInput from "ink-text-input";
|
|
31
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
32
|
+
var STEP_ORDER = ["lang", "org", "baseDir", "provider", "repos"];
|
|
33
|
+
function SetupWizard({
|
|
34
|
+
initial,
|
|
35
|
+
onAction = () => {
|
|
36
|
+
}
|
|
37
|
+
}) {
|
|
38
|
+
const base = defaultConfig();
|
|
39
|
+
const cwd = process.cwd();
|
|
40
|
+
const { exit } = useApp();
|
|
41
|
+
const [step, setStep] = useState(initial?.step ?? "lang");
|
|
42
|
+
const [lang, setLang] = useState(initial?.lang ?? base.lang);
|
|
43
|
+
const [langHighlight, setLangHighlight] = useState(initial?.lang ?? "en");
|
|
44
|
+
const [org, setOrg] = useState(initial?.org ?? base.org);
|
|
45
|
+
const [baseDir, setBaseDir] = useState(initial?.baseDir ?? base.repoBaseDir);
|
|
46
|
+
const [baseDirMode, setBaseDirMode] = useState("choose");
|
|
47
|
+
const [provider, setProvider] = useState(initial?.provider ?? "claude");
|
|
48
|
+
const [repoList, setRepoList] = useState(null);
|
|
49
|
+
const [selectedRepos, setSelectedRepos] = useState([]);
|
|
50
|
+
const [installed, setInstalled] = useState([]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (step === "done") {
|
|
53
|
+
onAction({ kind: "done" });
|
|
54
|
+
exit();
|
|
55
|
+
}
|
|
56
|
+
}, [step]);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (step === "repos" && repoList === null) {
|
|
59
|
+
fetchOrgRepoNames(org).then(setRepoList);
|
|
60
|
+
}
|
|
61
|
+
}, [step, repoList, org]);
|
|
62
|
+
const m = wizardT(lang);
|
|
63
|
+
const idx = step === "done" ? STEP_ORDER.length : STEP_ORDER.indexOf(step);
|
|
64
|
+
const providerLabel = provider === "claude" ? "Claude Code" : "Kiro";
|
|
65
|
+
const reposLabel = selectedRepos.length === 0 ? "\u2014" : selectedRepos.length <= 3 ? selectedRepos.join(", ") : `${selectedRepos.length} repos`;
|
|
66
|
+
const finalize = (repos) => {
|
|
67
|
+
setSelectedRepos(repos);
|
|
68
|
+
writeConfig({
|
|
69
|
+
...base,
|
|
70
|
+
lang,
|
|
71
|
+
org,
|
|
72
|
+
repoBaseDir: baseDir,
|
|
73
|
+
repos,
|
|
74
|
+
provider: { ...base.provider, default: provider }
|
|
75
|
+
});
|
|
76
|
+
setInstalled(
|
|
77
|
+
installAllSkills({
|
|
78
|
+
vars: { ORG: org, REPO_BASE: baseDir, PKG_SCOPE: base.packageScope, BASE_BRANCH: base.baseBranch },
|
|
79
|
+
targetRoot: skillsRootFor(provider)
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
setStep("done");
|
|
83
|
+
};
|
|
84
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
85
|
+
/* @__PURE__ */ jsx(Banner, { lang }),
|
|
86
|
+
/* @__PURE__ */ jsxs(Panel, { title: "\u273B Setup", color: theme.accent, children: [
|
|
87
|
+
idx > 0 && /* @__PURE__ */ jsx(Answered, { label: m.labels.language, value: lang === "en" ? "English" : "Fran\xE7ais" }),
|
|
88
|
+
idx > 1 && /* @__PURE__ */ jsx(Answered, { label: m.labels.org, value: org }),
|
|
89
|
+
idx > 2 && /* @__PURE__ */ jsx(Answered, { label: m.labels.repoDir, value: baseDir }),
|
|
90
|
+
idx > 3 && /* @__PURE__ */ jsx(Answered, { label: m.labels.provider, value: providerLabel }),
|
|
91
|
+
idx > 4 && /* @__PURE__ */ jsx(Answered, { label: m.labels.repos, value: reposLabel }),
|
|
92
|
+
step === "lang" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
93
|
+
/* @__PURE__ */ jsx(Text, { color: theme.text, children: langHighlight === "fr" ? "Choisissez votre langue" : "Choose your language" }),
|
|
94
|
+
/* @__PURE__ */ jsx(
|
|
95
|
+
Select,
|
|
96
|
+
{
|
|
97
|
+
items: [
|
|
98
|
+
{ label: "English", value: "en" },
|
|
99
|
+
{ label: "Fran\xE7ais", value: "fr" }
|
|
100
|
+
],
|
|
101
|
+
onHighlight: (value) => setLangHighlight(value),
|
|
102
|
+
onSelect: (value) => {
|
|
103
|
+
setLang(value);
|
|
104
|
+
setStep("org");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
] }),
|
|
109
|
+
step === "org" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
110
|
+
/* @__PURE__ */ jsx(Text, { color: theme.text, children: m.orgPrompt }),
|
|
111
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
112
|
+
/* @__PURE__ */ jsx(Text, { color: theme.dim, children: "org: " }),
|
|
113
|
+
/* @__PURE__ */ jsx(TextInput, { value: org, onChange: setOrg, onSubmit: () => setStep("baseDir") })
|
|
114
|
+
] })
|
|
115
|
+
] }),
|
|
116
|
+
step === "baseDir" && baseDirMode === "choose" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
117
|
+
/* @__PURE__ */ jsx(Text, { color: theme.text, children: m.baseDirPrompt }),
|
|
118
|
+
/* @__PURE__ */ jsx(
|
|
119
|
+
Select,
|
|
120
|
+
{
|
|
121
|
+
items: [
|
|
122
|
+
{ label: m.useCurrentDir, value: "cwd" },
|
|
123
|
+
{ label: m.enterPath, value: "custom" }
|
|
124
|
+
],
|
|
125
|
+
onSelect: (value) => {
|
|
126
|
+
if (value === "cwd") {
|
|
127
|
+
setBaseDir(cwd);
|
|
128
|
+
setStep("provider");
|
|
129
|
+
} else {
|
|
130
|
+
setBaseDirMode("custom");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
] }),
|
|
136
|
+
step === "baseDir" && baseDirMode === "custom" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
137
|
+
/* @__PURE__ */ jsx(Text, { color: theme.text, children: m.pathPrompt }),
|
|
138
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
139
|
+
/* @__PURE__ */ jsx(Text, { color: theme.dim, children: "path: " }),
|
|
140
|
+
/* @__PURE__ */ jsx(TextInput, { value: baseDir, onChange: setBaseDir, onSubmit: () => setStep("provider") })
|
|
141
|
+
] })
|
|
142
|
+
] }),
|
|
143
|
+
step === "provider" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
144
|
+
/* @__PURE__ */ jsx(Text, { color: theme.text, children: m.providerPrompt }),
|
|
145
|
+
/* @__PURE__ */ jsx(Text, { color: theme.dim, children: m.providerLoginNote }),
|
|
146
|
+
/* @__PURE__ */ jsx(
|
|
147
|
+
Select,
|
|
148
|
+
{
|
|
149
|
+
items: [
|
|
150
|
+
{ label: "Claude Code", value: "claude" },
|
|
151
|
+
{ label: "Kiro", value: "kiro" }
|
|
152
|
+
],
|
|
153
|
+
onSelect: (value) => {
|
|
154
|
+
const p = value;
|
|
155
|
+
setProvider(p);
|
|
156
|
+
onAction({
|
|
157
|
+
kind: "login",
|
|
158
|
+
provider: p,
|
|
159
|
+
state: { lang, org, baseDir, provider: p, step: "repos" }
|
|
160
|
+
});
|
|
161
|
+
exit();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
] }),
|
|
166
|
+
step === "repos" && (repoList === null ? /* @__PURE__ */ jsxs(Box, { children: [
|
|
167
|
+
/* @__PURE__ */ jsx(Spinner, {}),
|
|
168
|
+
/* @__PURE__ */ jsxs(Text, { color: theme.dim, children: [
|
|
169
|
+
" ",
|
|
170
|
+
m.reposLoading
|
|
171
|
+
] })
|
|
172
|
+
] }) : repoList.length === 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
173
|
+
/* @__PURE__ */ jsx(Text, { color: theme.warn, children: m.reposEmpty }),
|
|
174
|
+
/* @__PURE__ */ jsx(Select, { items: [{ label: m.continueLabel, value: "continue" }], onSelect: () => finalize([]) })
|
|
175
|
+
] }) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
176
|
+
/* @__PURE__ */ jsx(Text, { color: theme.text, children: m.reposPrompt }),
|
|
177
|
+
/* @__PURE__ */ jsx(MultiSelect, { items: repoList, hint: m.reposHint, onConfirm: (sel) => finalize(sel) })
|
|
178
|
+
] })),
|
|
179
|
+
step === "done" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
180
|
+
/* @__PURE__ */ jsx(KeyVal, { label: m.labels.saved, value: configPath() }),
|
|
181
|
+
installed.length > 0 && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
182
|
+
/* @__PURE__ */ jsx(Text, { color: theme.dim, children: m.skillsInstalled }),
|
|
183
|
+
installed.map((r) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
184
|
+
/* @__PURE__ */ jsx(Text, { color: theme.success, children: "\u2713" }),
|
|
185
|
+
" ",
|
|
186
|
+
r.installName,
|
|
187
|
+
" ",
|
|
188
|
+
/* @__PURE__ */ jsxs(Text, { color: theme.dim, children: [
|
|
189
|
+
"(",
|
|
190
|
+
r.action,
|
|
191
|
+
")"
|
|
192
|
+
] })
|
|
193
|
+
] }, r.installName))
|
|
194
|
+
] })
|
|
195
|
+
] })
|
|
196
|
+
] }),
|
|
197
|
+
step === "done" && /* @__PURE__ */ jsx(Text, { color: theme.dim, children: m.done })
|
|
198
|
+
] });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/ui/config-editor.tsx
|
|
202
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
203
|
+
import { Box as Box2, Text as Text2, useApp as useApp2 } from "ink";
|
|
204
|
+
import TextInput2 from "ink-text-input";
|
|
205
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
206
|
+
var providerName = (p) => p === "kiro" ? "Kiro" : p === "q" ? "Amazon Q" : "Claude Code";
|
|
207
|
+
function ConfigEditor({
|
|
208
|
+
config,
|
|
209
|
+
onAction = () => {
|
|
210
|
+
}
|
|
211
|
+
}) {
|
|
212
|
+
const { exit } = useApp2();
|
|
213
|
+
const [cfg, setCfg] = useState2(config);
|
|
214
|
+
const [field, setField] = useState2(null);
|
|
215
|
+
const [orgDraft, setOrgDraft] = useState2(config.org);
|
|
216
|
+
const [dirDraft, setDirDraft] = useState2(config.repoBaseDir);
|
|
217
|
+
const [repoList, setRepoList] = useState2(null);
|
|
218
|
+
const m = wizardT(cfg.lang);
|
|
219
|
+
useEffect2(() => {
|
|
220
|
+
if (field === "repos" && repoList === null) fetchOrgRepoNames(cfg.org).then(setRepoList);
|
|
221
|
+
}, [field, repoList, cfg.org]);
|
|
222
|
+
const apply = (next, opts = {}) => {
|
|
223
|
+
setCfg(next);
|
|
224
|
+
writeConfig(next);
|
|
225
|
+
if (opts.reinstall) {
|
|
226
|
+
installAllSkills({
|
|
227
|
+
vars: { ORG: next.org, REPO_BASE: next.repoBaseDir, PKG_SCOPE: next.packageScope, BASE_BRANCH: next.baseBranch },
|
|
228
|
+
targetRoot: skillsRootFor(next.provider.default)
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
setField(null);
|
|
232
|
+
};
|
|
233
|
+
if (field === null) {
|
|
234
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
235
|
+
/* @__PURE__ */ jsx2(Banner, { lang: cfg.lang }),
|
|
236
|
+
/* @__PURE__ */ jsxs2(Panel, { title: `\u273B ${m.editTitle}`, color: theme.accent, children: [
|
|
237
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: m.editHint }),
|
|
238
|
+
/* @__PURE__ */ jsx2(
|
|
239
|
+
Select,
|
|
240
|
+
{
|
|
241
|
+
items: [
|
|
242
|
+
{ label: `${m.labels.language}: ${cfg.lang === "fr" ? "Fran\xE7ais" : "English"}`, value: "language" },
|
|
243
|
+
{ label: `${m.labels.org}: ${cfg.org}`, value: "org" },
|
|
244
|
+
{ label: `${m.labels.repoDir}: ${cfg.repoBaseDir}`, value: "baseDir" },
|
|
245
|
+
{ label: `${m.labels.provider}: ${providerName(cfg.provider.default)}`, value: "provider" },
|
|
246
|
+
{ label: `${m.labels.repos} (${cfg.repos.length})`, value: "repos" },
|
|
247
|
+
{ label: m.editDone, value: "done" }
|
|
248
|
+
],
|
|
249
|
+
onSelect: (v) => {
|
|
250
|
+
if (v === "done") {
|
|
251
|
+
onAction({ kind: "done" });
|
|
252
|
+
exit();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (v === "org") setOrgDraft(cfg.org);
|
|
256
|
+
if (v === "baseDir") setDirDraft(cfg.repoBaseDir);
|
|
257
|
+
setField(v);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
),
|
|
261
|
+
cfg.repos.length > 0 && /* @__PURE__ */ jsx2(Chips, { items: cfg.repos })
|
|
262
|
+
] })
|
|
263
|
+
] });
|
|
264
|
+
}
|
|
265
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
266
|
+
/* @__PURE__ */ jsx2(Banner, {}),
|
|
267
|
+
/* @__PURE__ */ jsxs2(Panel, { title: `\u273B ${m.editTitle}`, color: theme.accent, children: [
|
|
268
|
+
field === "language" && /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
269
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.text, children: cfg.lang === "fr" ? "Choisissez votre langue" : "Choose your language" }),
|
|
270
|
+
/* @__PURE__ */ jsx2(
|
|
271
|
+
Select,
|
|
272
|
+
{
|
|
273
|
+
items: [
|
|
274
|
+
{ label: "English", value: "en" },
|
|
275
|
+
{ label: "Fran\xE7ais", value: "fr" }
|
|
276
|
+
],
|
|
277
|
+
onSelect: (v) => apply({ ...cfg, lang: v })
|
|
278
|
+
}
|
|
279
|
+
)
|
|
280
|
+
] }),
|
|
281
|
+
field === "org" && /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
282
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.text, children: m.orgPrompt }),
|
|
283
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
284
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "org: " }),
|
|
285
|
+
/* @__PURE__ */ jsx2(
|
|
286
|
+
TextInput2,
|
|
287
|
+
{
|
|
288
|
+
value: orgDraft,
|
|
289
|
+
onChange: setOrgDraft,
|
|
290
|
+
onSubmit: () => apply({ ...cfg, org: orgDraft.trim() || cfg.org }, { reinstall: true })
|
|
291
|
+
}
|
|
292
|
+
)
|
|
293
|
+
] })
|
|
294
|
+
] }),
|
|
295
|
+
field === "baseDir" && /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
296
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.text, children: m.pathPrompt }),
|
|
297
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
298
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: "path: " }),
|
|
299
|
+
/* @__PURE__ */ jsx2(
|
|
300
|
+
TextInput2,
|
|
301
|
+
{
|
|
302
|
+
value: dirDraft,
|
|
303
|
+
onChange: setDirDraft,
|
|
304
|
+
onSubmit: () => apply({ ...cfg, repoBaseDir: dirDraft.trim() || cfg.repoBaseDir }, { reinstall: true })
|
|
305
|
+
}
|
|
306
|
+
)
|
|
307
|
+
] })
|
|
308
|
+
] }),
|
|
309
|
+
field === "provider" && /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
310
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.text, children: m.providerPrompt }),
|
|
311
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.dim, children: m.providerLoginNote }),
|
|
312
|
+
/* @__PURE__ */ jsx2(
|
|
313
|
+
Select,
|
|
314
|
+
{
|
|
315
|
+
items: [
|
|
316
|
+
{ label: "Claude Code", value: "claude" },
|
|
317
|
+
{ label: "Kiro", value: "kiro" }
|
|
318
|
+
],
|
|
319
|
+
onSelect: (v) => {
|
|
320
|
+
const p = v;
|
|
321
|
+
if (p === cfg.provider.default) {
|
|
322
|
+
setField(null);
|
|
323
|
+
} else {
|
|
324
|
+
onAction({ kind: "changeProvider", provider: p });
|
|
325
|
+
exit();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
)
|
|
330
|
+
] }),
|
|
331
|
+
field === "repos" && (repoList === null ? /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
332
|
+
/* @__PURE__ */ jsx2(Spinner, {}),
|
|
333
|
+
/* @__PURE__ */ jsxs2(Text2, { color: theme.dim, children: [
|
|
334
|
+
" ",
|
|
335
|
+
m.reposLoading
|
|
336
|
+
] })
|
|
337
|
+
] }) : repoList.length === 0 ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
338
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.warn, children: m.reposEmpty }),
|
|
339
|
+
/* @__PURE__ */ jsx2(Select, { items: [{ label: m.continueLabel, value: "back" }], onSelect: () => setField(null) })
|
|
340
|
+
] }) : /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
341
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.text, children: m.reposPrompt }),
|
|
342
|
+
/* @__PURE__ */ jsx2(
|
|
343
|
+
MultiSelect,
|
|
344
|
+
{
|
|
345
|
+
items: repoList,
|
|
346
|
+
initial: cfg.repos,
|
|
347
|
+
hint: m.reposHint,
|
|
348
|
+
onConfirm: (sel) => apply({ ...cfg, repos: sel })
|
|
349
|
+
}
|
|
350
|
+
)
|
|
351
|
+
] }))
|
|
352
|
+
] })
|
|
353
|
+
] });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/lib/term.ts
|
|
357
|
+
function clearScreen() {
|
|
358
|
+
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// src/commands/setup.ts
|
|
362
|
+
async function runSetup() {
|
|
363
|
+
let wizardInitial = {};
|
|
364
|
+
for (; ; ) {
|
|
365
|
+
const cfg = readConfig();
|
|
366
|
+
if (!cfg) {
|
|
367
|
+
const action2 = await renderWizard(wizardInitial);
|
|
368
|
+
if (action2.kind === "done") return 0;
|
|
369
|
+
console.log("");
|
|
370
|
+
const result2 = await linkProvider(action2.provider, realLinkDeps());
|
|
371
|
+
if (!result2.installed) {
|
|
372
|
+
console.log(`
|
|
373
|
+
Setup cancelled \u2014 ${result2.provider} was not installed.`);
|
|
374
|
+
return 1;
|
|
375
|
+
}
|
|
376
|
+
clearScreen();
|
|
377
|
+
wizardInitial = action2.state;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
const action = await renderEditor(cfg);
|
|
381
|
+
if (action.kind === "done") return 0;
|
|
382
|
+
console.log("");
|
|
383
|
+
const result = await linkProvider(action.provider, realLinkDeps());
|
|
384
|
+
if (result.installed) {
|
|
385
|
+
const next = { ...cfg, provider: { ...cfg.provider, default: action.provider } };
|
|
386
|
+
writeConfig(next);
|
|
387
|
+
installAllSkills({
|
|
388
|
+
vars: { ORG: next.org, REPO_BASE: next.repoBaseDir, PKG_SCOPE: next.packageScope, BASE_BRANCH: next.baseBranch },
|
|
389
|
+
targetRoot: skillsRootFor(action.provider)
|
|
390
|
+
});
|
|
391
|
+
} else {
|
|
392
|
+
console.log(`
|
|
393
|
+
${result.provider} was not installed \u2014 provider unchanged.`);
|
|
394
|
+
}
|
|
395
|
+
clearScreen();
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
function renderWizard(initial) {
|
|
400
|
+
return new Promise((resolve) => {
|
|
401
|
+
let captured = { kind: "done" };
|
|
402
|
+
const app = render(createElement(SetupWizard, { initial, onAction: (a) => captured = a }));
|
|
403
|
+
void app.waitUntilExit().then(() => resolve(captured));
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
function renderEditor(config) {
|
|
407
|
+
return new Promise((resolve) => {
|
|
408
|
+
let captured = { kind: "done" };
|
|
409
|
+
const app = render(createElement(ConfigEditor, { config, onAction: (a) => captured = a }));
|
|
410
|
+
void app.waitUntilExit().then(() => resolve(captured));
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export {
|
|
415
|
+
clearScreen,
|
|
416
|
+
runSetup
|
|
417
|
+
};
|
|
418
|
+
//# sourceMappingURL=chunk-FDTKUXXE.js.map
|