@ryan_nookpi/pi-extension-setup-sh 0.1.0 → 0.2.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/README.md +13 -28
- package/context.ts +2 -7
- package/index.ts +12 -55
- package/package.json +3 -3
- package/widget.ts +5 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @ryan_nookpi/pi-extension-setup-sh
|
|
2
2
|
|
|
3
|
-
Automatically runs
|
|
3
|
+
Automatically runs `setup.sh` from the current pi working directory when a session starts.
|
|
4
4
|
|
|
5
5
|
This is useful for repositories that need a predictable bootstrap step before the agent starts editing, testing, or running commands.
|
|
6
6
|
|
|
@@ -12,52 +12,37 @@ pi install npm:@ryan_nookpi/pi-extension-setup-sh
|
|
|
12
12
|
|
|
13
13
|
## What it does
|
|
14
14
|
|
|
15
|
-
-
|
|
15
|
+
- looks for `setup.sh` only in the current working directory
|
|
16
16
|
- runs `setup.sh` automatically on session startup
|
|
17
17
|
- skips automatic reruns when the same `setup.sh` already completed successfully
|
|
18
|
+
- keeps a single `/setup-sh` command for manual control
|
|
18
19
|
- shows running, pending, failed, cancelled, and stale states in the pi UI
|
|
19
20
|
- keeps per-project logs and state under pi's setup-sh state directory
|
|
20
21
|
- prevents duplicate setup runs with a lock file
|
|
21
22
|
|
|
22
|
-
##
|
|
23
|
+
## Command
|
|
23
24
|
|
|
24
25
|
```text
|
|
25
26
|
/setup-sh
|
|
26
|
-
/setup-sh status
|
|
27
|
-
/setup-sh rerun
|
|
28
|
-
/setup-sh cancel
|
|
29
|
-
/setup-sh clear
|
|
30
|
-
/setup-sh help
|
|
31
27
|
```
|
|
32
28
|
|
|
33
|
-
|
|
29
|
+
`/setup-sh` is a toggle-style command:
|
|
34
30
|
|
|
35
|
-
|
|
31
|
+
- if no setup run exists yet, it starts `./setup.sh`
|
|
32
|
+
- if setup is currently running, it aborts the active run
|
|
33
|
+
- if setup already finished, it reruns `./setup.sh`
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Shows the current setup state and recent log lines.
|
|
40
|
-
|
|
41
|
-
### `/setup-sh rerun`
|
|
42
|
-
|
|
43
|
-
Runs `setup.sh` again even if the previous run succeeded.
|
|
44
|
-
|
|
45
|
-
### `/setup-sh cancel`
|
|
46
|
-
|
|
47
|
-
Sends a termination signal to the active setup process.
|
|
48
|
-
|
|
49
|
-
### `/setup-sh clear`
|
|
50
|
-
|
|
51
|
-
Hides the setup widget for the current session.
|
|
35
|
+
No subcommands are registered.
|
|
52
36
|
|
|
53
37
|
## Behavior
|
|
54
38
|
|
|
55
|
-
- `setup.sh` is executed with `/bin/zsh` from the
|
|
39
|
+
- `setup.sh` is executed with `/bin/zsh` from the current working directory.
|
|
56
40
|
- A successful automatic run is remembered by the script hash, so changing `setup.sh` allows it to run again.
|
|
57
41
|
- Long-running setup output becomes `pending` in the UI when logs have been idle for a while.
|
|
58
|
-
- Failed and stale states stay visible
|
|
42
|
+
- Failed and stale states stay visible and can be rerun with `/setup-sh`.
|
|
43
|
+
- If the current directory does not contain `setup.sh`, `/setup-sh` reports that it was not found in the current folder.
|
|
59
44
|
|
|
60
45
|
## Requirements
|
|
61
46
|
|
|
62
47
|
- pi
|
|
63
|
-
- a readable `setup.sh` file in the
|
|
48
|
+
- a readable `setup.sh` file in the current pi working directory
|
package/context.ts
CHANGED
|
@@ -49,13 +49,8 @@ export function findSetupPath(cwd: string): string | null {
|
|
|
49
49
|
// Keep resolved cwd when the directory cannot be canonicalized yet.
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (isExecutableOrReadable(candidate)) return candidate;
|
|
55
|
-
const parent = path.dirname(current);
|
|
56
|
-
if (parent === current) return null;
|
|
57
|
-
current = parent;
|
|
58
|
-
}
|
|
52
|
+
const candidate = path.join(current, "setup.sh");
|
|
53
|
+
return isExecutableOrReadable(candidate) ? candidate : null;
|
|
59
54
|
}
|
|
60
55
|
|
|
61
56
|
export function resolveSetupContext(cwd: string): SetupContext | null {
|
package/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExtensionAPI, ExtensionContext } from "@
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { PENDING_AFTER_MS, STATE_ROOT, STATUS_KEY, WIDGET_KEY, WIDGET_REFRESH_MS } from "./constants.js";
|
|
3
3
|
import { displayPath, findSetupPath, repoKeyFor, resolveSetupContext } from "./context.js";
|
|
4
4
|
import { cancelSetup, createWrapperScript, startSetup } from "./runner.js";
|
|
@@ -10,22 +10,10 @@ import {
|
|
|
10
10
|
installWidget,
|
|
11
11
|
refreshWatcher,
|
|
12
12
|
renderWidgetLine,
|
|
13
|
-
showStatus,
|
|
14
13
|
stopWatcher,
|
|
15
14
|
type Watcher,
|
|
16
15
|
} from "./widget.js";
|
|
17
16
|
|
|
18
|
-
function commandHelp(): string {
|
|
19
|
-
return [
|
|
20
|
-
"/setup-sh [status|rerun|cancel|clear]",
|
|
21
|
-
"- 인자 없음: 현재 폴더의 setup.sh 실행 또는 실행 상태 표시",
|
|
22
|
-
"- status: 상세 상태와 로그 tail 표시",
|
|
23
|
-
"- rerun: 성공/실패 상태와 무관하게 재실행",
|
|
24
|
-
"- cancel: 실행 중인 setup.sh 종료",
|
|
25
|
-
"- clear: 현재 세션의 setup widget 숨김",
|
|
26
|
-
].join("\n");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
17
|
async function notifyStartResult(ctx: ExtensionContext, result: StartResult): Promise<void> {
|
|
30
18
|
if (result.kind === "started") {
|
|
31
19
|
ctx.ui.notify(`setup.sh 실행 시작: ${displayPath(result.context.repoRoot)}`, "info");
|
|
@@ -61,10 +49,9 @@ export const __test__ = {
|
|
|
61
49
|
|
|
62
50
|
export default function setupShExtension(pi: ExtensionAPI): void {
|
|
63
51
|
let watcher: Watcher | null = null;
|
|
64
|
-
const hiddenRepoKeys = new Set<string>();
|
|
65
52
|
|
|
66
53
|
async function watch(ctx: ExtensionContext, context: SetupContext): Promise<void> {
|
|
67
|
-
if (!ctx.hasUI
|
|
54
|
+
if (!ctx.hasUI) return;
|
|
68
55
|
stopWatcher(ctx, watcher);
|
|
69
56
|
watcher = {
|
|
70
57
|
context,
|
|
@@ -81,10 +68,7 @@ export default function setupShExtension(pi: ExtensionAPI): void {
|
|
|
81
68
|
|
|
82
69
|
async function runOrAttach(ctx: ExtensionContext, mode: StartMode): Promise<StartResult> {
|
|
83
70
|
const result = await startSetup(ctx.cwd, mode);
|
|
84
|
-
if (result.kind !== "no-setup" && result.kind !== "failed")
|
|
85
|
-
hiddenRepoKeys.delete(result.context.repoKey);
|
|
86
|
-
await watch(ctx, result.context);
|
|
87
|
-
}
|
|
71
|
+
if (result.kind !== "no-setup" && result.kind !== "failed") await watch(ctx, result.context);
|
|
88
72
|
return result;
|
|
89
73
|
}
|
|
90
74
|
|
|
@@ -118,55 +102,28 @@ export default function setupShExtension(pi: ExtensionAPI): void {
|
|
|
118
102
|
});
|
|
119
103
|
|
|
120
104
|
pi.registerCommand("setup-sh", {
|
|
121
|
-
description: "Run or
|
|
122
|
-
getArgumentCompletions: (prefix: string) => {
|
|
123
|
-
const values = ["status", "rerun", "cancel", "clear", "help"];
|
|
124
|
-
return values
|
|
125
|
-
.filter((value) => value.startsWith(prefix.trim()))
|
|
126
|
-
.map((value) => ({ value, label: value, description: `/setup-sh ${value}` }));
|
|
127
|
-
},
|
|
105
|
+
description: "Run, rerun, or abort ./setup.sh in the current folder",
|
|
128
106
|
handler: async (args, ctx) => {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (action === "help") {
|
|
132
|
-
ctx.ui.notify(commandHelp(), "info");
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
if (!context) {
|
|
136
|
-
ctx.ui.notify("현재 폴더 또는 상위 폴더에서 setup.sh를 찾지 못했습니다.", "warning");
|
|
107
|
+
if (args.trim().length > 0) {
|
|
108
|
+
ctx.ui.notify("사용법: /setup-sh", "warning");
|
|
137
109
|
return;
|
|
138
110
|
}
|
|
139
111
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
watcher = null;
|
|
144
|
-
ctx.ui.notify("setup.sh widget을 숨겼습니다.", "info");
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (action === "status") {
|
|
149
|
-
hiddenRepoKeys.delete(context.repoKey);
|
|
150
|
-
await watch(ctx, context);
|
|
151
|
-
await showStatus(ctx, context);
|
|
112
|
+
const context = resolveSetupContext(ctx.cwd);
|
|
113
|
+
if (!context) {
|
|
114
|
+
ctx.ui.notify("현재 폴더에서 setup.sh를 찾지 못했습니다.", "warning");
|
|
152
115
|
return;
|
|
153
116
|
}
|
|
154
117
|
|
|
155
|
-
|
|
118
|
+
const current = await finalizeRunIfNeeded(context);
|
|
119
|
+
if (current?.status === "running") {
|
|
156
120
|
const message = await cancelSetup(context);
|
|
157
|
-
hiddenRepoKeys.delete(context.repoKey);
|
|
158
121
|
await watch(ctx, context);
|
|
159
122
|
ctx.ui.notify(message, "warning");
|
|
160
123
|
return;
|
|
161
124
|
}
|
|
162
125
|
|
|
163
|
-
|
|
164
|
-
ctx.ui.notify(commandHelp(), "warning");
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const mode: StartMode = action === "rerun" ? "rerun" : "manual";
|
|
169
|
-
await notifyStartResult(ctx, await runOrAttach(ctx, mode));
|
|
126
|
+
await notifyStartResult(ctx, await runOrAttach(ctx, current ? "rerun" : "manual"));
|
|
170
127
|
},
|
|
171
128
|
});
|
|
172
129
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryan_nookpi/pi-extension-setup-sh",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Auto-run setup.sh extension for pi.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
]
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@
|
|
37
|
-
"@
|
|
36
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
37
|
+
"@earendil-works/pi-tui": "*"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
package/widget.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ExtensionContext, Theme } from "@
|
|
2
|
-
import type { TUI } from "@
|
|
3
|
-
import { truncateToWidth } from "@
|
|
1
|
+
import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { TUI } from "@earendil-works/pi-tui";
|
|
3
|
+
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
4
4
|
import { PENDING_AFTER_MS, STATUS_KEY, WIDGET_KEY } from "./constants.js";
|
|
5
5
|
import { displayPath } from "./context.js";
|
|
6
6
|
import { finalizeRunIfNeeded } from "./state.js";
|
|
@@ -49,11 +49,11 @@ export function formatSnapshotLine(snapshot: Snapshot, theme: Pick<Theme, "fg">)
|
|
|
49
49
|
|
|
50
50
|
if (snapshot.status === "success") return `${theme.fg("success", "✅")} setup.sh done ${duration} · ${repo}`;
|
|
51
51
|
if (snapshot.status === "failed") {
|
|
52
|
-
return `${theme.fg("error", "❌")} setup.sh failed${exit} · ${repo}${suffix} · /setup-sh
|
|
52
|
+
return `${theme.fg("error", "❌")} setup.sh failed${exit} · ${repo}${suffix} · /setup-sh`;
|
|
53
53
|
}
|
|
54
54
|
if (snapshot.status === "cancelled") return `${theme.fg("warning", "⚠️")} setup.sh cancelled · ${repo}${suffix}`;
|
|
55
55
|
if (snapshot.status === "stale") {
|
|
56
|
-
return `${theme.fg("warning", "⚠️")} setup.sh stale · ${repo}${suffix} · /setup-sh
|
|
56
|
+
return `${theme.fg("warning", "⚠️")} setup.sh stale · ${repo}${suffix} · /setup-sh`;
|
|
57
57
|
}
|
|
58
58
|
if (snapshot.status === "pending") {
|
|
59
59
|
return `${theme.fg("warning", "⏳")} setup.sh pending${owner} ${duration} · ${repo}${suffix}`;
|