@punks/cli 1.0.4 → 1.0.5

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
@@ -67,7 +67,7 @@ Current scaffold-managed global tools:
67
67
  - `portless`
68
68
  - `skills`
69
69
 
70
- On startup, `punks` checks npm for a newer CLI version. When it can infer whether the installed binary came from Bun, pnpm, or npm, it updates itself with the same global package manager; otherwise it prints a manual update hint. If the `dp-cli` operator skill is installed in the current project or globally, startup also asks the `skills` CLI to update that skill. Set `DP_NO_UPDATE_CHECK=1` or `DP_NO_SKILL_UPDATE_CHECK=1` to skip those checks.
70
+ On startup, `punks` starts best-effort checks for a newer CLI version and the `dp-cli` operator skill. CLI package updates prompt before running the inferred package-manager command. The `dp-cli` skill is auto-installed when missing and auto-updated when present through targeted `skills add ... --skill dp-cli` / `skills update dp-cli ...` commands only; startup never runs or suggests plain root `skills update`. Set `DP_NO_UPDATE_CHECK=1` or `DP_NO_SKILL_UPDATE_CHECK=1` to skip those checks.
71
71
 
72
72
  ## Publishing
73
73
 
package/dist/index.js CHANGED
@@ -10883,6 +10883,10 @@ var require_public_api = __commonJS((exports) => {
10883
10883
  exports.stringify = stringify;
10884
10884
  });
10885
10885
 
10886
+ // src/index.ts
10887
+ import { spawnSync as spawnSync2 } from "child_process";
10888
+ import { createInterface as createInterface3 } from "readline/promises";
10889
+
10886
10890
  // node_modules/.pnpm/effect@3.19.19/node_modules/effect/dist/esm/Array.js
10887
10891
  var exports_Array = {};
10888
10892
  __export(exports_Array, {
@@ -42283,16 +42287,15 @@ var toolCatalog = [
42283
42287
  }
42284
42288
  ];
42285
42289
 
42286
- // src/version.ts
42287
- var cliPackageName = "@punks/cli";
42288
- var cliVersion = "1.0.4";
42289
-
42290
+ // package.json
42291
+ var name = "@punks/cli";
42292
+ var version = "1.0.5";
42290
42293
  // src/baseline/bundled.ts
42291
42294
  var bundledBaseline = {
42292
42295
  summary: {
42293
42296
  source: "bundled",
42294
42297
  channel: "bundled",
42295
- version: cliVersion,
42298
+ version,
42296
42299
  tag: null,
42297
42300
  commit: null,
42298
42301
  sha256: null
@@ -42778,8 +42781,8 @@ var collectPackageNames = (pkg) => {
42778
42781
  if (!record2) {
42779
42782
  return;
42780
42783
  }
42781
- for (const name of Object.keys(record2)) {
42782
- names.add(name);
42784
+ for (const name2 of Object.keys(record2)) {
42785
+ names.add(name2);
42783
42786
  }
42784
42787
  };
42785
42788
  addEntries(pkg.dependencies);
@@ -43570,12 +43573,13 @@ var renderHint = (value5) => paint(brandPalette.muted, value5);
43570
43573
  var renderSelfUpdateNotice = ({
43571
43574
  currentVersion,
43572
43575
  latestVersion,
43573
- packageName
43576
+ packageName,
43577
+ command
43574
43578
  }) => renderPanel("CLI update available", [
43575
43579
  `${packageName} ${currentVersion} is behind ${latestVersion}.`,
43576
43580
  "",
43577
43581
  "usage:",
43578
- ` \u2022 run \`npm install -g ${packageName}@latest\` to update this CLI`
43582
+ ` \u2022 run \`${command?.join(" ") ?? `npm install -g ${packageName}@latest`}\` to update this CLI`
43579
43583
  ].join(`
43580
43584
  `));
43581
43585
  var renderQuestion = (value5) => [
@@ -44075,8 +44079,8 @@ var packsForWorkspace = ({
44075
44079
  const dependencyNames = manifest?.dependencies ?? [];
44076
44080
  const dependencies = new Set(dependencyNames);
44077
44081
  const selectedPacks = new Set(finalPacks);
44078
- const hasDependency = (name) => dependencies.has(name);
44079
- const hasDependencyPrefix = (prefix) => dependencyNames.some((name) => name.startsWith(prefix));
44082
+ const hasDependency = (name2) => dependencies.has(name2);
44083
+ const hasDependencyPrefix = (prefix) => dependencyNames.some((name2) => name2.startsWith(prefix));
44080
44084
  const hasBackendDependency = () => hasDependency("better-auth") || hasDependency("drizzle-orm") || hasDependency("drizzle-kit") || hasDependency("elysia") || hasDependencyPrefix("@trpc/");
44081
44085
  const hasFrontendDependency = () => hasDependency("next") || hasDependency("react") || hasDependency("react-dom") || hasDependency("@tanstack/react-query");
44082
44086
  const hasFrontendWorkspaceName = () => relativeWorkspacePath.split(path8.sep).some((segment) => segment === "frontend" || segment === "ui" || segment === "web") || (manifest?.packageName?.replace(/^@[^/]+\//u, "").split(/[^a-zA-Z0-9]+/u).filter((segment) => segment.length > 0).map((segment) => segment.toLowerCase()).some((segment) => segment === "frontend" || segment === "ui" || segment === "web") ?? false);
@@ -44682,7 +44686,7 @@ var writeScaffoldOutput = ({
44682
44686
  lintAssets,
44683
44687
  subagentManifestSpec,
44684
44688
  baseline: baseline.summary,
44685
- cliVersion,
44689
+ version,
44686
44690
  managedFiles,
44687
44691
  generatedFiles: manifestGeneratedFiles
44688
44692
  })
@@ -46418,7 +46422,7 @@ var managedFileSchema = Struct({
46418
46422
  });
46419
46423
  var scaffoldManifestSchema = parseJson(Struct({
46420
46424
  repoShapeMode: optional(String$),
46421
- cliVersion: optional(String$),
46425
+ version: optional(String$),
46422
46426
  finalPacks: Array$(String$),
46423
46427
  baseline: optional(Unknown),
46424
46428
  generatedFiles: optional(Array$(String$)),
@@ -46835,7 +46839,7 @@ var checkSelfUpdate = async ({
46835
46839
  updateAvailable: isNewerVersion(latestVersion, currentVersion)
46836
46840
  };
46837
46841
  };
46838
- var commandExists2 = (command) => spawnSync("which", [command], { stdio: "ignore" }).status === 0;
46842
+ var commandExists2 = (command) => spawnSync("which", [command], { stdio: "ignore", timeout: 750 }).status === 0;
46839
46843
  var detectCliInstallPackageManager = ({
46840
46844
  entrypoint = process.argv[1] ?? "",
46841
46845
  userAgent = process.env.npm_config_user_agent
@@ -46907,39 +46911,47 @@ var autoUpdateCliIfStale = async ({
46907
46911
  packageName,
46908
46912
  tag: tag4
46909
46913
  });
46910
- const result = spawnSync(command[0], command.slice(1), {
46911
- stdio: "inherit",
46912
- shell: false
46913
- });
46914
- if (result.status !== 0) {
46915
- return {
46916
- status: "failed",
46917
- check: check2,
46918
- packageManager,
46919
- command
46920
- };
46921
- }
46922
46914
  return {
46923
- status: "updated",
46915
+ status: "available",
46924
46916
  check: check2,
46925
46917
  packageManager,
46926
46918
  command
46927
46919
  };
46928
46920
  };
46929
46921
  var parseSkillList = (value5) => {
46930
- const parsed = JSON.parse(value5);
46931
- if (!Array.isArray(parsed)) {
46932
- return [];
46922
+ try {
46923
+ const parsed = JSON.parse(value5);
46924
+ if (!Array.isArray(parsed)) {
46925
+ return [];
46926
+ }
46927
+ return parsed.flatMap((entry) => {
46928
+ if (typeof entry === "object" && entry !== null && "name" in entry && "path" in entry && "scope" in entry && typeof entry.name === "string" && typeof entry.path === "string" && typeof entry.scope === "string") {
46929
+ return [{
46930
+ name: entry.name,
46931
+ path: entry.path,
46932
+ scope: entry.scope
46933
+ }];
46934
+ }
46935
+ return [];
46936
+ });
46937
+ } catch {
46938
+ return parseTextSkillList(value5);
46933
46939
  }
46934
- return parsed.flatMap((entry) => {
46935
- if (typeof entry === "object" && entry !== null && "name" in entry && "path" in entry && "scope" in entry && typeof entry.name === "string" && typeof entry.path === "string" && typeof entry.scope === "string") {
46936
- return [{
46937
- name: entry.name,
46938
- path: entry.path,
46939
- scope: entry.scope
46940
- }];
46940
+ };
46941
+ var stripAnsi = (value5) => value5.replace(/\x1B\[[0-?]*[ -/]*[@-~]/gu, "");
46942
+ var parseTextSkillList = (value5) => {
46943
+ const scope5 = value5.includes("Global Skills") ? "global" : "project";
46944
+ return stripAnsi(value5).split(`
46945
+ `).flatMap((line4) => {
46946
+ const match18 = line4.match(/^([^\s]+)\s+(.+)$/u);
46947
+ if (match18 === null || match18[1] === "Project" || match18[1] === "Global") {
46948
+ return [];
46941
46949
  }
46942
- return [];
46950
+ return [{
46951
+ name: match18[1],
46952
+ path: match18[2].trim(),
46953
+ scope: scope5
46954
+ }];
46943
46955
  });
46944
46956
  };
46945
46957
  var listSkills = (scope5) => {
@@ -46948,66 +46960,82 @@ var listSkills = (scope5) => {
46948
46960
  encoding: "utf8",
46949
46961
  shell: false,
46950
46962
  stdio: ["ignore", "pipe", "ignore"],
46951
- timeout: 5000
46963
+ timeout: 1500
46952
46964
  });
46953
46965
  if (result.status !== 0) {
46954
46966
  return [];
46955
46967
  }
46956
- try {
46957
- return parseSkillList(result.stdout);
46958
- } catch {
46959
- return [];
46960
- }
46961
- };
46962
- var updateDpCliSkill = (scope5) => {
46963
- const args2 = scope5 === "global" ? ["update", "dp-cli", "--global", "--yes"] : ["update", "dp-cli", "--project", "--yes"];
46964
- const result = spawnSync("skills", args2, {
46968
+ return parseSkillList(result.stdout);
46969
+ };
46970
+ var skillUpdateCommand = (scope5) => scope5 === "global" ? ["skills", "update", "dp-cli", "--global", "--yes"] : ["skills", "update", "dp-cli", "--project", "--yes"];
46971
+ var skillInstallCommand = () => [
46972
+ "skills",
46973
+ "add",
46974
+ "wearedevpunks/skills",
46975
+ "--global",
46976
+ "--skill",
46977
+ "dp-cli",
46978
+ "--yes"
46979
+ ];
46980
+ var runSkillsCommand = (command) => {
46981
+ const result = spawnSync(command[0], command.slice(1), {
46965
46982
  stdio: "ignore",
46966
46983
  shell: false,
46967
46984
  timeout: 30000
46968
46985
  });
46969
46986
  return result.status === 0;
46970
46987
  };
46988
+ var updateDpCliSkill = (scope5) => runSkillsCommand(skillUpdateCommand(scope5));
46971
46989
  var installDpCliSkillGlobal = () => {
46972
- const result = spawnSync("skills", [
46973
- "add",
46974
- "wearedevpunks/skills",
46975
- "--global",
46976
- "--skill",
46977
- "dp-cli",
46978
- "--yes"
46979
- ], {
46980
- stdio: "ignore",
46981
- shell: false,
46982
- timeout: 30000
46983
- });
46984
- return result.status === 0;
46990
+ return runSkillsCommand(skillInstallCommand());
46985
46991
  };
46986
- var ensureDpCliSkillPresent = () => {
46992
+ var checkDpCliSkillPresence = () => {
46987
46993
  if (!commandExists2("skills")) {
46988
46994
  return {
46989
46995
  skillsCliAvailable: false,
46990
46996
  detected: false,
46991
46997
  project: false,
46992
- global: false,
46993
- installedGlobal: false,
46994
- updatedProject: false,
46995
- updatedGlobal: false
46998
+ global: false
46996
46999
  };
46997
47000
  }
46998
47001
  const projectBeforeInstall = listSkills("project").some((skill) => skill.name === "dp-cli");
46999
47002
  const globalBeforeInstall = listSkills("global").some((skill) => skill.name === "dp-cli");
47000
- const installedGlobal = projectBeforeInstall || globalBeforeInstall ? false : installDpCliSkillGlobal();
47001
47003
  const project3 = projectBeforeInstall;
47002
- const global = globalBeforeInstall || installedGlobal || listSkills("global").some((skill) => skill.name === "dp-cli");
47004
+ const global = globalBeforeInstall;
47003
47005
  return {
47004
47006
  skillsCliAvailable: true,
47005
47007
  detected: project3 || global,
47006
47008
  project: project3,
47007
- global,
47008
- installedGlobal,
47009
- updatedProject: project3 ? updateDpCliSkill("project") : false,
47010
- updatedGlobal: global ? updateDpCliSkill("global") : false
47009
+ global
47010
+ };
47011
+ };
47012
+ var maintainDpCliSkill = () => {
47013
+ const presence = checkDpCliSkillPresence();
47014
+ if (!presence.skillsCliAvailable) {
47015
+ return {
47016
+ ...presence,
47017
+ installedGlobal: false,
47018
+ updatedProject: false,
47019
+ updatedGlobal: false
47020
+ };
47021
+ }
47022
+ if (!presence.detected) {
47023
+ const installedGlobal = installDpCliSkillGlobal();
47024
+ return {
47025
+ skillsCliAvailable: true,
47026
+ detected: installedGlobal,
47027
+ project: false,
47028
+ global: installedGlobal,
47029
+ installedGlobal,
47030
+ updatedProject: false,
47031
+ updatedGlobal: installedGlobal ? updateDpCliSkill("global") : false
47032
+ };
47033
+ }
47034
+ return {
47035
+ ...presence,
47036
+ installedGlobal: false,
47037
+ updatedProject: presence.project ? updateDpCliSkill("project") : false,
47038
+ updatedGlobal: presence.global ? updateDpCliSkill("global") : false
47011
47039
  };
47012
47040
  };
47013
47041
 
@@ -47015,31 +47043,72 @@ var ensureDpCliSkillPresent = () => {
47015
47043
  var app = exports_Command.make("punks", {}, () => exports_Effect.sync(() => console.log(renderCommandGuide()))).pipe(exports_Command.withDescription("Devpunks AI scaffolding CLI."), exports_Command.withSubcommands([scaffoldCommand, updateCommand]));
47016
47044
  var cli = exports_Command.run(app, {
47017
47045
  name: "punks",
47018
- version: cliVersion
47046
+ version
47019
47047
  });
47020
47048
  var shouldCheckSelfUpdate = () => process.env.DP_NO_UPDATE_CHECK !== "1" && process.env.CI === undefined && process.argv.includes("--help") === false && process.argv.includes("-h") === false && process.argv.includes("--version") === false && process.argv.includes("-v") === false;
47021
- var selfUpdateNotice = exports_Effect.promise(async () => {
47022
- if (!shouldCheckSelfUpdate()) {
47023
- return;
47024
- }
47049
+ var shouldCheckDpCliSkillUpdate = () => process.env.DP_NO_SKILL_UPDATE_CHECK !== "1" && process.env.CI === undefined && process.argv.includes("--help") === false && process.argv.includes("-h") === false && process.argv.includes("--version") === false && process.argv.includes("-v") === false;
47050
+ var runCliUpdateCheck = async () => {
47025
47051
  const result = await autoUpdateCliIfStale({
47026
- currentVersion: cliVersion,
47027
- packageName: cliPackageName,
47052
+ currentVersion: version,
47053
+ packageName: name,
47028
47054
  tag: process.env.DP_UPDATE_TAG ?? "latest"
47029
47055
  });
47030
- if (result.status === "manager-not-detected" || result.status === "failed") {
47056
+ if (result.status === "available" || result.status === "manager-not-detected" || result.status === "failed") {
47031
47057
  console.error(renderSelfUpdateNotice({
47032
47058
  currentVersion: result.check.currentVersion,
47033
47059
  latestVersion: result.check.latestVersion,
47034
- packageName: result.check.packageName
47060
+ packageName: result.check.packageName,
47061
+ command: result.command
47035
47062
  }));
47036
47063
  }
47037
- });
47038
- var shouldCheckDpCliSkillUpdate = () => process.env.DP_NO_SKILL_UPDATE_CHECK !== "1" && process.env.CI === undefined && process.argv.includes("--help") === false && process.argv.includes("-h") === false && process.argv.includes("--version") === false && process.argv.includes("-v") === false;
47039
- var dpCliSkillUpdate = exports_Effect.sync(() => {
47040
- if (!shouldCheckDpCliSkillUpdate()) {
47064
+ if (result.status !== "available" || process.stdin.isTTY !== true) {
47041
47065
  return;
47042
47066
  }
47043
- ensureDpCliSkillPresent();
47044
- });
47045
- exports_Effect.zipRight(exports_Effect.zipRight(selfUpdateNotice, dpCliSkillUpdate), cli(process.argv)).pipe(exports_Effect.provide(exports_Layer.mergeAll(exports_BunContext.layer)), exports_BunRuntime.runMain);
47067
+ const prompt4 = createInterface3({
47068
+ input: process.stdin,
47069
+ output: process.stderr
47070
+ });
47071
+ try {
47072
+ const answer = await prompt4.question("Update punks CLI now? [y/N]: ");
47073
+ if (!["y", "yes"].includes(answer.trim().toLowerCase())) {
47074
+ return;
47075
+ }
47076
+ console.error(`punks startup checks: running \`${result.command.join(" ")}\``);
47077
+ const update5 = spawnSync2(result.command[0], result.command.slice(1), {
47078
+ stdio: "inherit",
47079
+ shell: false
47080
+ });
47081
+ if (update5.status !== 0) {
47082
+ console.error("punks startup checks: CLI update failed.");
47083
+ }
47084
+ } finally {
47085
+ prompt4.close();
47086
+ }
47087
+ };
47088
+ var runDpCliSkillCheck = () => {
47089
+ const skillResult = maintainDpCliSkill();
47090
+ if (!skillResult.skillsCliAvailable) {
47091
+ return;
47092
+ }
47093
+ if (skillResult.installedGlobal) {
47094
+ console.error("punks startup checks: installed `dp-cli` skill globally.");
47095
+ }
47096
+ if (skillResult.updatedProject || skillResult.updatedGlobal) {
47097
+ console.error("punks startup checks: updated `dp-cli` skill.");
47098
+ }
47099
+ };
47100
+ var runStartupChecks = async () => {
47101
+ const checkCli = shouldCheckSelfUpdate();
47102
+ const checkSkill = shouldCheckDpCliSkillUpdate();
47103
+ if (!checkCli && !checkSkill) {
47104
+ return;
47105
+ }
47106
+ if (checkSkill) {
47107
+ setTimeout(runDpCliSkillCheck, 0);
47108
+ }
47109
+ if (checkCli) {
47110
+ await runCliUpdateCheck();
47111
+ }
47112
+ };
47113
+ runStartupChecks().catch(() => {});
47114
+ cli(process.argv).pipe(exports_Effect.provide(exports_Layer.mergeAll(exports_BunContext.layer)), exports_BunRuntime.runMain);
package/docs/README.md CHANGED
@@ -11,8 +11,7 @@ Implementation notes:
11
11
  - distributed skill assets live in `skills/`
12
12
  - runtime projection/writing logic lives in `src/scaffold/`
13
13
  - `punks update` refreshes scaffold-managed assets from `.devpunks/scaffold-manifest.json`
14
- - CLI startup checks npm's `latest` dist-tag for `@punks/cli` and prints a self-update notice when the installed CLI is behind. Set `DP_NO_UPDATE_CHECK=1` to skip the check or `DP_UPDATE_TAG=next` to compare against another dist-tag.
15
- - CLI startup also checks for the `dp-cli` skill through the `skills` CLI, installing it globally when absent and updating it when present. Set `DP_NO_SKILL_UPDATE_CHECK=1` to skip this best-effort pass.
14
+ - CLI startup checks npm's `latest` dist-tag and prompts before CLI package updates; it auto-installs/updates only the named `dp-cli` skill through targeted `skills add ... --skill dp-cli` / `skills update dp-cli ...` commands. Set `DP_NO_UPDATE_CHECK=1` or `DP_NO_SKILL_UPDATE_CHECK=1` to skip those checks, and `DP_UPDATE_TAG=next` to compare against another dist-tag.
16
15
  - baseline releases use `baseline/stable/*` GitHub release tags, separate from npm executable tags such as `v1.0.1`
17
16
  - shared neutral hook and sync assets live in `src/data/hooks/` and `src/data/scripts/`
18
17
  - scaffolded required tools always include `portless` and `skills` so generated guidance can standardize local dev origins and keep skill entrypoints up to date
@@ -217,11 +217,11 @@ The npm account must have publish access to `@punks/cli`; otherwise npm may repo
217
217
 
218
218
  ## CLI Self-Update Detection
219
219
 
220
- The CLI also performs best-effort startup checks on normal command startup. It checks the npm package version for `@punks/cli`, and it checks for the `dp-cli` skill through the global `skills` CLI, installing it when absent and updating it when present. These are separate from `punks update`, which updates scaffold-managed repo assets.
220
+ The CLI also starts best-effort startup checks on normal command startup. It checks the npm package version for `@punks/cli`, and it checks the `dp-cli` skill through the `skills` CLI. CLI package updates require an interactive `y/N` confirmation before running the inferred package-manager command. The `dp-cli` skill is auto-installed when missing and auto-updated when present, but only through targeted `skills add ... --skill dp-cli` / `skills update dp-cli ...` commands. Startup must never run or suggest plain root `skills update`. These checks are separate from `punks update`, which updates scaffold-managed repo assets.
221
221
 
222
222
  - checks `https://registry.npmjs.org/%40punks%2Fcli/latest`
223
223
  - compares that dist-tag version with the bundled CLI version
224
- - prints `npm install -g @punks/cli@latest` only when the registry version is newer
224
+ - prompts to run the inferred package-manager command only when the registry version is newer
225
225
  - silently skips the notice when npm is unreachable, the registry response is invalid, or the command is `--help` / `--version`
226
226
  - skips in CI and when `DP_NO_UPDATE_CHECK=1`
227
227
  - supports `DP_UPDATE_TAG=next` for canary/operator testing against another dist-tag
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@punks/cli",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Devpunks AI scaffolding CLI",
5
5
  "type": "module",
6
6
  "bin": {