@schilderlabs/pitown 0.1.2 → 0.2.6

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 CHANGED
@@ -4,6 +4,12 @@ The installable Pi Town CLI.
4
4
 
5
5
  Pi Town is an experimental orchestration tool for Pi.
6
6
 
7
+ ## Credits
8
+
9
+ Pi Town is built on top of Pi. Credit to [Mario Zechner](https://github.com/badlogic) and the Pi project for the underlying coding agent runtime Pi Town orchestrates.
10
+
11
+ Pi Town was also inspired by [Gastown](https://github.com/steveyegge/gastown). Credit to [Steve Yegge](https://github.com/steveyegge) for pushing on multi-agent orchestration ideas that made this project worth exploring.
12
+
7
13
  For the full project overview, roadmap, and architecture context, see the main repo:
8
14
 
9
15
  - https://github.com/schilderlabs/pitown
@@ -19,6 +25,7 @@ npm install -g @mariozechner/pi-coding-agent
19
25
  Verify Pi first:
20
26
 
21
27
  ```bash
28
+ pitown doctor
22
29
  pi -p "hello"
23
30
  ```
24
31
 
@@ -26,10 +33,28 @@ pi -p "hello"
26
33
 
27
34
  ```bash
28
35
  pitown --help
36
+ pitown
37
+ pitown mayor
38
+ pitown mayor "plan the next milestones"
29
39
  pitown run --repo /path/to/repo --plan /path/to/private/plans --goal "continue from current scaffold state"
30
40
  pitown status
31
41
  ```
32
42
 
43
+ If you are already inside a repo, `pitown` and `pitown mayor` use the current working repo by default.
44
+
45
+ The main workflow is:
46
+
47
+ 1. `cd` into a repo
48
+ 2. run `pitown` or `pitown mayor`
49
+ 3. use `/plan` inside the mayor session when you want a read-only plan first
50
+ 4. use `pitown board`, `pitown peek mayor`, or `pitown msg mayor "..."` as needed
51
+
52
+ Inside the mayor session:
53
+
54
+ - `/plan` toggles read-only planning mode
55
+ - `/todos` shows the captured numbered plan
56
+ - leaving `/plan` returns the mayor to normal execution and delegation mode
57
+
33
58
  ## Runtime storage
34
59
 
35
60
  By default, Pi Town stores local runtime state under `~/.pi-town` and keeps private plans outside the target repo.
@@ -1,55 +1,9 @@
1
- import { existsSync, readFileSync, realpathSync } from "node:fs";
1
+ import { a as runCommandSync, r as assertSuccess } from "./entrypoint-WBAQmFbT.mjs";
2
+ import { existsSync, readFileSync } from "node:fs";
2
3
  import { basename, dirname, isAbsolute, join, resolve } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
4
  import { homedir } from "node:os";
5
5
  import { createHash } from "node:crypto";
6
- import { spawnSync } from "node:child_process";
7
6
 
8
- //#region src/entrypoint.ts
9
- function normalizePath(path) {
10
- if (!path) return null;
11
- try {
12
- return realpathSync(path);
13
- } catch {
14
- return resolve(path);
15
- }
16
- }
17
- function isDirectExecution(fileUrl, argv1 = process.argv[1]) {
18
- const modulePath = normalizePath(fileURLToPath(fileUrl));
19
- const invokedPath = normalizePath(argv1);
20
- return modulePath !== null && invokedPath !== null && modulePath === invokedPath;
21
- }
22
-
23
- //#endregion
24
- //#region ../core/src/shell.ts
25
- function runCommandSync(command, args, options) {
26
- const result = spawnSync(command, args, {
27
- cwd: options?.cwd,
28
- env: options?.env,
29
- encoding: "utf-8"
30
- });
31
- const errorText = result.error instanceof Error ? `${result.error.message}
32
- ` : "";
33
- return {
34
- stdout: result.stdout ?? "",
35
- stderr: `${errorText}${result.stderr ?? ""}`,
36
- exitCode: result.status ?? 1
37
- };
38
- }
39
- function assertCommandAvailable(command) {
40
- const result = spawnSync(command, ["--help"], {
41
- encoding: "utf-8",
42
- stdio: "ignore"
43
- });
44
- if (result.error instanceof Error) throw new Error(result.error.message);
45
- }
46
- function assertSuccess(result, context) {
47
- if (result.exitCode === 0) return;
48
- const details = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
49
- throw new Error(`${context} failed${details ? `\n${details}` : ""}`);
50
- }
51
-
52
- //#endregion
53
7
  //#region ../core/src/repo.ts
54
8
  function gitResult(cwd, args) {
55
9
  return runCommandSync("git", args, { cwd });
@@ -142,6 +96,7 @@ function parseCliFlags(argv) {
142
96
  const flags = { help: false };
143
97
  for (let index = 0; index < argv.length; index += 1) {
144
98
  const arg = argv[index];
99
+ if (arg === void 0) continue;
145
100
  if (arg === "--help" || arg === "-h") {
146
101
  flags.help = true;
147
102
  continue;
@@ -183,6 +138,30 @@ function parseCliFlags(argv) {
183
138
  }
184
139
  return flags;
185
140
  }
141
+ function parseOptionalRepoFlag(argv) {
142
+ const rest = [];
143
+ let repo;
144
+ for (let index = 0; index < argv.length; index += 1) {
145
+ const arg = argv[index];
146
+ if (arg === void 0) continue;
147
+ if (arg.startsWith("--repo=")) {
148
+ repo = arg.slice(7);
149
+ continue;
150
+ }
151
+ if (arg === "--repo") {
152
+ const value = argv[index + 1];
153
+ if (!value) throw new Error("Missing value for --repo");
154
+ repo = value;
155
+ index += 1;
156
+ continue;
157
+ }
158
+ rest.push(arg);
159
+ }
160
+ return repo === void 0 ? { rest } : {
161
+ repo,
162
+ rest
163
+ };
164
+ }
186
165
  function loadUserConfig() {
187
166
  const configPath = getUserConfigPath();
188
167
  if (!existsSync(configPath)) return {};
@@ -202,5 +181,5 @@ function resolveRunConfig(argv) {
202
181
  }
203
182
 
204
183
  //#endregion
205
- export { getRepoArtifactsDir as a, createRepoSlug as c, getRepoRoot as d, assertCommandAvailable as f, getRecommendedPlanDir as i, getCurrentBranch as l, isDirectExecution as m, resolveRunConfig as n, getRepoLatestRunPointerPath as o, runCommandSync as p, getLatestRunPointerPath as r, getTownHomeDir as s, parseCliFlags as t, getRepoIdentity as u };
206
- //# sourceMappingURL=config-Bw-mNdF5.mjs.map
184
+ export { getRecommendedPlanDir as a, getReposRootDir as c, createRepoSlug as d, getCurrentBranch as f, isGitRepo as h, getLatestRunPointerPath as i, getTownHomeDir as l, getRepoRoot as m, parseOptionalRepoFlag as n, getRepoArtifactsDir as o, getRepoIdentity as p, resolveRunConfig as r, getRepoLatestRunPointerPath as s, parseCliFlags as t, getUserConfigPath as u };
185
+ //# sourceMappingURL=config-BG1v4iIi.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-BG1v4iIi.mjs","names":[],"sources":["../../core/src/repo.ts","../src/paths.ts","../src/config.ts"],"sourcesContent":["import { createHash } from \"node:crypto\"\nimport { existsSync } from \"node:fs\"\nimport { basename, resolve } from \"node:path\"\nimport { assertSuccess, runCommandSync } from \"./shell.js\"\n\nfunction gitResult(cwd: string, args: string[]) {\n\treturn runCommandSync(\"git\", args, { cwd })\n}\n\nfunction sanitize(value: string): string {\n\treturn value.replace(/[^a-zA-Z0-9._-]+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"repo\"\n}\n\nexport function isGitRepo(cwd: string): boolean {\n\tconst result = gitResult(cwd, [\"rev-parse\", \"--is-inside-work-tree\"])\n\treturn result.exitCode === 0 && result.stdout.trim() === \"true\"\n}\n\nexport function getRepoRoot(cwd: string): string {\n\tif (!isGitRepo(cwd)) return resolve(cwd)\n\tconst result = gitResult(cwd, [\"rev-parse\", \"--show-toplevel\"])\n\tassertSuccess(result, \"git rev-parse --show-toplevel\")\n\treturn resolve(result.stdout.trim())\n}\n\nexport function getCurrentBranch(cwd: string): string | null {\n\tif (!isGitRepo(cwd)) return null\n\tconst result = gitResult(cwd, [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"])\n\tif (result.exitCode !== 0) return null\n\tconst branch = result.stdout.trim()\n\treturn branch || null\n}\n\nexport function getRepoIdentity(cwd: string): string {\n\tif (!isGitRepo(cwd)) return resolve(cwd)\n\n\tconst remote = gitResult(cwd, [\"config\", \"--get\", \"remote.origin.url\"])\n\tconst remoteValue = remote.stdout.trim()\n\tif (remote.exitCode === 0 && remoteValue) return remoteValue\n\n\tconst root = gitResult(cwd, [\"rev-parse\", \"--show-toplevel\"])\n\tassertSuccess(root, \"git rev-parse --show-toplevel\")\n\tconst commonDir = gitResult(cwd, [\"rev-parse\", \"--git-common-dir\"])\n\tassertSuccess(commonDir, \"git rev-parse --git-common-dir\")\n\n\tconst rootPath = resolve(root.stdout.trim())\n\tconst commonDirPath = commonDir.stdout.trim()\n\treturn `${basename(rootPath)}:${rootPath}:${existsSync(commonDirPath) ? resolve(commonDirPath) : commonDirPath}`\n}\n\nexport function createRepoSlug(repoId: string, repoRoot: string): string {\n\tconst name = sanitize(basename(repoRoot))\n\tconst digest = createHash(\"sha1\").update(repoId).digest(\"hex\").slice(0, 8)\n\treturn `${name}-${digest}`\n}\n","import { homedir } from \"node:os\"\nimport { join } from \"node:path\"\n\nexport function getTownHomeDir(): string {\n\treturn join(homedir(), \".pi-town\")\n}\n\nexport function getUserConfigPath(): string {\n\treturn join(getTownHomeDir(), \"config.json\")\n}\n\nexport function getPlansRootDir(): string {\n\treturn join(getTownHomeDir(), \"plans\")\n}\n\nexport function getReposRootDir(): string {\n\treturn join(getTownHomeDir(), \"repos\")\n}\n\nexport function getRepoArtifactsDir(repoSlug: string): string {\n\treturn join(getReposRootDir(), repoSlug)\n}\n\nexport function getRepoAgentsDir(repoSlug: string): string {\n\treturn join(getRepoArtifactsDir(repoSlug), \"agents\")\n}\n\nexport function getLatestRunPointerPath(): string {\n\treturn join(getTownHomeDir(), \"latest-run.json\")\n}\n\nexport function getRepoLatestRunPointerPath(repoSlug: string): string {\n\treturn join(getRepoArtifactsDir(repoSlug), \"latest-run.json\")\n}\n\nexport function getRecommendedPlanDir(repoSlug: string): string {\n\treturn join(getPlansRootDir(), repoSlug)\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, isAbsolute, resolve } from \"node:path\"\nimport { homedir } from \"node:os\"\nimport { getUserConfigPath } from \"./paths.js\"\n\nconst DEFAULT_GOAL = \"continue from current scaffold state\"\n\nexport interface CliFlags {\n\trepo?: string\n\tplan?: string\n\tgoal?: string\n\thelp: boolean\n}\n\nexport interface OptionalRepoFlagResult {\n\trepo?: string\n\trest: string[]\n}\n\ninterface UserConfig {\n\trepo?: string\n\tplan?: string\n\tgoal?: string\n}\n\nexport interface ResolvedRunConfig {\n\trepo: string\n\tplan: string | null\n\tgoal: string\n\tconfigPath: string\n}\n\nfunction expandHome(value: string): string {\n\tif (value === \"~\") return homedir()\n\tif (value.startsWith(\"~/\")) return resolve(homedir(), value.slice(2))\n\treturn value\n}\n\nfunction resolvePathValue(value: string | undefined, baseDir: string): string | undefined {\n\tif (!value) return undefined\n\tconst expanded = expandHome(value)\n\treturn isAbsolute(expanded) ? resolve(expanded) : resolve(baseDir, expanded)\n}\n\nexport function parseCliFlags(argv: string[]): CliFlags {\n\tconst flags: CliFlags = { help: false }\n\n\tfor (let index = 0; index < argv.length; index += 1) {\n\t\tconst arg = argv[index]\n\t\tif (arg === undefined) continue\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tflags.help = true\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg.startsWith(\"--repo=\")) {\n\t\t\tflags.repo = arg.slice(\"--repo=\".length)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === \"--repo\") {\n\t\t\tconst value = argv[index + 1]\n\t\t\tif (!value) throw new Error(\"Missing value for --repo\")\n\t\t\tflags.repo = value\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg.startsWith(\"--plan=\")) {\n\t\t\tflags.plan = arg.slice(\"--plan=\".length)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === \"--plan\") {\n\t\t\tconst value = argv[index + 1]\n\t\t\tif (!value) throw new Error(\"Missing value for --plan\")\n\t\t\tflags.plan = value\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg.startsWith(\"--goal=\")) {\n\t\t\tflags.goal = arg.slice(\"--goal=\".length)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === \"--goal\") {\n\t\t\tconst value = argv[index + 1]\n\t\t\tif (!value) throw new Error(\"Missing value for --goal\")\n\t\t\tflags.goal = value\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\t}\n\n\t\tthrow new Error(`Unknown argument: ${arg}`)\n\t}\n\n\treturn flags\n}\n\nexport function parseOptionalRepoFlag(argv: string[]): OptionalRepoFlagResult {\n\tconst rest: string[] = []\n\tlet repo: string | undefined\n\n\tfor (let index = 0; index < argv.length; index += 1) {\n\t\tconst arg = argv[index]\n\t\tif (arg === undefined) continue\n\n\t\tif (arg.startsWith(\"--repo=\")) {\n\t\t\trepo = arg.slice(\"--repo=\".length)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (arg === \"--repo\") {\n\t\t\tconst value = argv[index + 1]\n\t\t\tif (!value) throw new Error(\"Missing value for --repo\")\n\t\t\trepo = value\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\t}\n\n\t\trest.push(arg)\n\t}\n\n\treturn repo === undefined ? { rest } : { repo, rest }\n}\n\nexport function loadUserConfig(): UserConfig {\n\tconst configPath = getUserConfigPath()\n\tif (!existsSync(configPath)) return {}\n\treturn JSON.parse(readFileSync(configPath, \"utf-8\")) as UserConfig\n}\n\nexport function resolveRunConfig(argv: string[]): ResolvedRunConfig {\n\tconst flags = parseCliFlags(argv)\n\tconst configPath = getUserConfigPath()\n\tconst userConfig = loadUserConfig()\n\tconst configDir = dirname(configPath)\n\n\tconst repo =\n\t\tresolvePathValue(flags.repo, process.cwd()) ??\n\t\tresolvePathValue(userConfig.repo, configDir) ??\n\t\tresolve(process.cwd())\n\tconst plan = resolvePathValue(flags.plan, process.cwd()) ?? resolvePathValue(userConfig.plan, configDir) ?? null\n\tconst goal = flags.goal ?? userConfig.goal ?? DEFAULT_GOAL\n\n\treturn {\n\t\trepo,\n\t\tplan,\n\t\tgoal,\n\t\tconfigPath,\n\t}\n}\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ //#region src/doctor.d.ts
2
+ interface DoctorResult {
3
+ ok: boolean;
4
+ }
5
+ declare function runDoctor(): DoctorResult;
6
+ //#endregion
7
+ export { DoctorResult, runDoctor };
8
+ //# sourceMappingURL=doctor.d.mts.map
@@ -0,0 +1,42 @@
1
+ import { n as detectPiAuthFailure, t as createPiAuthHelpMessage } from "./pi-C7HRNjBG.mjs";
2
+ import { a as runCommandSync, t as isDirectExecution } from "./entrypoint-WBAQmFbT.mjs";
3
+
4
+ //#region src/doctor.ts
5
+ function runDoctor() {
6
+ console.log("[pitown] doctor");
7
+ if (runCommandSync("pi", ["--help"]).exitCode !== 0) {
8
+ console.log("- pi cli: missing");
9
+ console.log("- install: npm install -g @mariozechner/pi-coding-agent");
10
+ console.log("- verify: pi -p \"hello\"");
11
+ return { ok: false };
12
+ }
13
+ const check = runCommandSync("pi", [
14
+ "--no-session",
15
+ "-p",
16
+ "hello"
17
+ ]);
18
+ if (check.exitCode === 0) {
19
+ console.log("- pi cli: installed");
20
+ console.log("- pi auth: ready");
21
+ console.log("- status: ok");
22
+ return { ok: true };
23
+ }
24
+ if (detectPiAuthFailure(check.stderr, check.stdout)) {
25
+ console.log("- pi cli: installed");
26
+ console.log("- pi auth: not ready");
27
+ console.log(`- note: ${createPiAuthHelpMessage()}`);
28
+ return { ok: false };
29
+ }
30
+ console.log("- pi cli: installed");
31
+ console.log("- pi check: failed");
32
+ if (check.stderr.trim()) console.log(`- stderr: ${check.stderr.trim()}`);
33
+ else if (check.stdout.trim()) console.log(`- stdout: ${check.stdout.trim()}`);
34
+ return { ok: false };
35
+ }
36
+ if (isDirectExecution(import.meta.url)) {
37
+ if (!runDoctor().ok) process.exitCode = 1;
38
+ }
39
+
40
+ //#endregion
41
+ export { runDoctor };
42
+ //# sourceMappingURL=doctor.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.mjs","names":[],"sources":["../src/doctor.ts"],"sourcesContent":["import { createPiAuthHelpMessage, detectPiAuthFailure, runCommandSync } from \"../../core/src/index.js\"\nimport { isDirectExecution } from \"./entrypoint.js\"\n\nexport interface DoctorResult {\n\tok: boolean\n}\n\nexport function runDoctor(): DoctorResult {\n\tconsole.log(\"[pitown] doctor\")\n\n\tconst availability = runCommandSync(\"pi\", [\"--help\"])\n\tif (availability.exitCode !== 0) {\n\t\tconsole.log(\"- pi cli: missing\")\n\t\tconsole.log(\"- install: npm install -g @mariozechner/pi-coding-agent\")\n\t\tconsole.log('- verify: pi -p \"hello\"')\n\t\treturn { ok: false }\n\t}\n\n\tconst check = runCommandSync(\"pi\", [\"--no-session\", \"-p\", \"hello\"])\n\tif (check.exitCode === 0) {\n\t\tconsole.log(\"- pi cli: installed\")\n\t\tconsole.log(\"- pi auth: ready\")\n\t\tconsole.log(\"- status: ok\")\n\t\treturn { ok: true }\n\t}\n\n\tif (detectPiAuthFailure(check.stderr, check.stdout)) {\n\t\tconsole.log(\"- pi cli: installed\")\n\t\tconsole.log(\"- pi auth: not ready\")\n\t\tconsole.log(`- note: ${createPiAuthHelpMessage()}`)\n\t\treturn { ok: false }\n\t}\n\n\tconsole.log(\"- pi cli: installed\")\n\tconsole.log(\"- pi check: failed\")\n\tif (check.stderr.trim()) console.log(`- stderr: ${check.stderr.trim()}`)\n\telse if (check.stdout.trim()) console.log(`- stdout: ${check.stdout.trim()}`)\n\treturn { ok: false }\n}\n\nif (isDirectExecution(import.meta.url)) {\n\tconst result = runDoctor()\n\tif (!result.ok) process.exitCode = 1\n}\n"],"mappings":";;;;AAOA,SAAgB,YAA0B;AACzC,SAAQ,IAAI,kBAAkB;AAG9B,KADqB,eAAe,MAAM,CAAC,SAAS,CAAC,CACpC,aAAa,GAAG;AAChC,UAAQ,IAAI,oBAAoB;AAChC,UAAQ,IAAI,0DAA0D;AACtE,UAAQ,IAAI,4BAA0B;AACtC,SAAO,EAAE,IAAI,OAAO;;CAGrB,MAAM,QAAQ,eAAe,MAAM;EAAC;EAAgB;EAAM;EAAQ,CAAC;AACnE,KAAI,MAAM,aAAa,GAAG;AACzB,UAAQ,IAAI,sBAAsB;AAClC,UAAQ,IAAI,mBAAmB;AAC/B,UAAQ,IAAI,eAAe;AAC3B,SAAO,EAAE,IAAI,MAAM;;AAGpB,KAAI,oBAAoB,MAAM,QAAQ,MAAM,OAAO,EAAE;AACpD,UAAQ,IAAI,sBAAsB;AAClC,UAAQ,IAAI,uBAAuB;AACnC,UAAQ,IAAI,WAAW,yBAAyB,GAAG;AACnD,SAAO,EAAE,IAAI,OAAO;;AAGrB,SAAQ,IAAI,sBAAsB;AAClC,SAAQ,IAAI,qBAAqB;AACjC,KAAI,MAAM,OAAO,MAAM,CAAE,SAAQ,IAAI,aAAa,MAAM,OAAO,MAAM,GAAG;UAC/D,MAAM,OAAO,MAAM,CAAE,SAAQ,IAAI,aAAa,MAAM,OAAO,MAAM,GAAG;AAC7E,QAAO,EAAE,IAAI,OAAO;;AAGrB,IAAI,kBAAkB,OAAO,KAAK,IAAI,EAErC;KAAI,CADW,WAAW,CACd,GAAI,SAAQ,WAAW"}
@@ -0,0 +1,61 @@
1
+ import { realpathSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ //#region ../core/src/shell.ts
7
+ function runCommandSync(command, args, options) {
8
+ const result = spawnSync(command, args, {
9
+ cwd: options?.cwd,
10
+ env: options?.env,
11
+ encoding: "utf-8"
12
+ });
13
+ const errorText = result.error instanceof Error ? `${result.error.message}
14
+ ` : "";
15
+ return {
16
+ stdout: result.stdout ?? "",
17
+ stderr: `${errorText}${result.stderr ?? ""}`,
18
+ exitCode: result.status ?? 1
19
+ };
20
+ }
21
+ function assertCommandAvailable(command) {
22
+ const result = spawnSync(command, ["--help"], {
23
+ encoding: "utf-8",
24
+ stdio: "ignore"
25
+ });
26
+ if (result.error instanceof Error) throw new Error(result.error.message);
27
+ }
28
+ function runCommandInteractive(command, args, options) {
29
+ const result = spawnSync(command, args, {
30
+ cwd: options?.cwd,
31
+ env: options?.env,
32
+ stdio: "inherit"
33
+ });
34
+ if (result.error instanceof Error) throw new Error(result.error.message);
35
+ return result.status ?? 1;
36
+ }
37
+ function assertSuccess(result, context) {
38
+ if (result.exitCode === 0) return;
39
+ const details = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
40
+ throw new Error(`${context} failed${details ? `\n${details}` : ""}`);
41
+ }
42
+
43
+ //#endregion
44
+ //#region src/entrypoint.ts
45
+ function normalizePath(path) {
46
+ if (!path) return null;
47
+ try {
48
+ return realpathSync(path);
49
+ } catch {
50
+ return resolve(path);
51
+ }
52
+ }
53
+ function isDirectExecution(fileUrl, argv1 = process.argv[1]) {
54
+ const modulePath = normalizePath(fileURLToPath(fileUrl));
55
+ const invokedPath = normalizePath(argv1);
56
+ return modulePath !== null && invokedPath !== null && modulePath === invokedPath;
57
+ }
58
+
59
+ //#endregion
60
+ export { runCommandSync as a, runCommandInteractive as i, assertCommandAvailable as n, assertSuccess as r, isDirectExecution as t };
61
+ //# sourceMappingURL=entrypoint-WBAQmFbT.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entrypoint-WBAQmFbT.mjs","names":[],"sources":["../../core/src/shell.ts","../src/entrypoint.ts"],"sourcesContent":["import { spawnSync } from \"node:child_process\"\n\nexport interface CommandResult {\n\tstdout: string\n\tstderr: string\n\texitCode: number\n}\n\nexport function runCommandSync(\n\tcommand: string,\n\targs: string[],\n\toptions?: { cwd?: string; env?: NodeJS.ProcessEnv },\n): CommandResult {\n\tconst result = spawnSync(command, args, {\n\t\tcwd: options?.cwd,\n\t\tenv: options?.env,\n\t\tencoding: \"utf-8\",\n\t})\n\tconst errorText = result.error instanceof Error ? `${result.error.message}\n` : \"\"\n\n\treturn {\n\t\tstdout: result.stdout ?? \"\",\n\t\tstderr: `${errorText}${result.stderr ?? \"\"}`,\n\t\texitCode: result.status ?? 1,\n\t}\n}\n\nexport function assertCommandAvailable(command: string) {\n\tconst result = spawnSync(command, [\"--help\"], {\n\t\tencoding: \"utf-8\",\n\t\tstdio: \"ignore\",\n\t})\n\n\tif (result.error instanceof Error) {\n\t\tthrow new Error(result.error.message)\n\t}\n}\n\nexport function runCommandInteractive(\n\tcommand: string,\n\targs: string[],\n\toptions?: { cwd?: string; env?: NodeJS.ProcessEnv },\n): number {\n\tconst result = spawnSync(command, args, {\n\t\tcwd: options?.cwd,\n\t\tenv: options?.env,\n\t\tstdio: \"inherit\",\n\t})\n\n\tif (result.error instanceof Error) {\n\t\tthrow new Error(result.error.message)\n\t}\n\n\treturn result.status ?? 1\n}\n\nexport function assertSuccess(result: CommandResult, context: string) {\n\tif (result.exitCode === 0) return\n\tconst details = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join(\"\\n\")\n\tthrow new Error(`${context} failed${details ? `\\n${details}` : \"\"}`)\n}\n","import { realpathSync } from \"node:fs\"\nimport { resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nfunction normalizePath(path: string | undefined): string | null {\n\tif (!path) return null\n\ttry {\n\t\treturn realpathSync(path)\n\t} catch {\n\t\treturn resolve(path)\n\t}\n}\n\nexport function isDirectExecution(fileUrl: string, argv1 = process.argv[1]): boolean {\n\tconst modulePath = normalizePath(fileURLToPath(fileUrl))\n\tconst invokedPath = normalizePath(argv1)\n\treturn modulePath !== null && invokedPath !== null && modulePath === invokedPath\n}\n"],"mappings":";;;;;;AAQA,SAAgB,eACf,SACA,MACA,SACgB;CAChB,MAAM,SAAS,UAAU,SAAS,MAAM;EACvC,KAAK,SAAS;EACd,KAAK,SAAS;EACd,UAAU;EACV,CAAC;CACF,MAAM,YAAY,OAAO,iBAAiB,QAAQ,GAAG,OAAO,MAAM,QAAQ;IACvE;AAEH,QAAO;EACN,QAAQ,OAAO,UAAU;EACzB,QAAQ,GAAG,YAAY,OAAO,UAAU;EACxC,UAAU,OAAO,UAAU;EAC3B;;AAGF,SAAgB,uBAAuB,SAAiB;CACvD,MAAM,SAAS,UAAU,SAAS,CAAC,SAAS,EAAE;EAC7C,UAAU;EACV,OAAO;EACP,CAAC;AAEF,KAAI,OAAO,iBAAiB,MAC3B,OAAM,IAAI,MAAM,OAAO,MAAM,QAAQ;;AAIvC,SAAgB,sBACf,SACA,MACA,SACS;CACT,MAAM,SAAS,UAAU,SAAS,MAAM;EACvC,KAAK,SAAS;EACd,KAAK,SAAS;EACd,OAAO;EACP,CAAC;AAEF,KAAI,OAAO,iBAAiB,MAC3B,OAAM,IAAI,MAAM,OAAO,MAAM,QAAQ;AAGtC,QAAO,OAAO,UAAU;;AAGzB,SAAgB,cAAc,QAAuB,SAAiB;AACrE,KAAI,OAAO,aAAa,EAAG;CAC3B,MAAM,UAAU,CAAC,OAAO,OAAO,MAAM,EAAE,OAAO,OAAO,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,KAAK,KAAK;AACvF,OAAM,IAAI,MAAM,GAAG,QAAQ,SAAS,UAAU,KAAK,YAAY,KAAK;;;;;ACxDrE,SAAS,cAAc,MAAyC;AAC/D,KAAI,CAAC,KAAM,QAAO;AAClB,KAAI;AACH,SAAO,aAAa,KAAK;SAClB;AACP,SAAO,QAAQ,KAAK;;;AAItB,SAAgB,kBAAkB,SAAiB,QAAQ,QAAQ,KAAK,IAAa;CACpF,MAAM,aAAa,cAAc,cAAc,QAAQ,CAAC;CACxD,MAAM,cAAc,cAAc,MAAM;AACxC,QAAO,eAAe,QAAQ,gBAAgB,QAAQ,eAAe"}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region src/index.d.ts
2
- declare function printHelp(): void;
2
+ declare function printHelp(showAdvanced?: boolean): void;
3
3
  declare function runCli(argv?: string[]): void;
4
4
  //#endregion
5
5
  export { printHelp, runCli };