@tommy-ca/lazypi 0.6.5 → 0.8.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 CHANGED
@@ -13,7 +13,7 @@ LazyPi will:
13
13
  1. Install `pi` for you if it isn't installed yet.
14
14
  2. Ask if you want to install all the packages or choose which to install.
15
15
 
16
- That setup is the harness core — isolated sub-agents, a structured ask gate, skill visibility, $ skill mention, a long-objective gate, side chat, context budgeting, code simplification review, web research, and FFF search. Optional extras (skill arguments, memory, MCP, interactive shell overlays, research loops, themes) install on demand with `pi install`.
16
+ That setup is the harness core — isolated sub-agents, a structured ask gate, skill visibility, $ skill mention, a long-objective gate, side chat, context budgeting, code simplification review, web research, FFF search, a workflow engine for sub-agent fan-out, and ponytail discipline review. Optional extras (skill arguments, memory, MCP, interactive shell overlays, research loops, themes) install on demand with `pi install`.
17
17
 
18
18
  That's it. Once done - run `pi` and experience a feature rich coding agent experience.
19
19
 
@@ -54,6 +54,12 @@ There is nothing to "uninstall" for LazyPi itself — `npx` doesn't leave it aro
54
54
 
55
55
  Run the built-in health check with `npx @tommy-ca/lazypi doctor`.
56
56
 
57
+ **Stale `bunx` after a release?** bun caches resolved package versions, so a
58
+ bare `bunx @tommy-ca/lazypi` can keep serving the previous release until the
59
+ cache is cleared. Run `bun pm cache rm`, pin `@tommy-ca/lazypi@latest`, and
60
+ check `npx @tommy-ca/lazypi --version` to see which release is actually
61
+ running.
62
+
57
63
  ## Site / docs
58
64
 
59
65
  The site at [lazypi.org](https://lazypi.org) lives in `docs/` and is a Jekyll site compiled by GitHub Pages automatically on push to `master`.
package/bin/lazypi.mjs CHANGED
@@ -44,6 +44,8 @@ export const PACKAGES = [
44
44
  { id: "simplify", category: "core", source: "npm:pi-simplify", essential: true, description: "Code simplify review", hint: "Reviews recently changed code for clarity, consistency, and maintainability." },
45
45
  { id: "web-access", category: "core", source: "npm:pi-web-access", essential: true, description: "Web search and page fetch", hint: "Built-in web search and URL fetching." },
46
46
  { id: "fff", category: "core", source: "npm:@ff-labs/pi-fff", essential: true, description: "FFF fuzzy search", hint: "Additive fffind / ffgrep / fff-multi-grep beside the built-in tools; /fff-health checks the index." },
47
+ { id: "dynamic-workflows", category: "core", source: "npm:@quintinshaw/pi-dynamic-workflows", essential: true, description: "Workflow engine", hint: "Fan work out across subagents: /workflows TUI, deep-research, resume, token accounting." },
48
+ { id: "ponytail", category: "core", source: "npm:@dietrichgebert/ponytail", essential: true, description: "Lazy senior dev mode", hint: "Enforces minimal, stdlib-first code; /ponytail review, audit, and a debt ledger." },
47
49
  ];
48
50
 
49
51
  // ---------------------------------------------------------------------------
@@ -96,6 +98,7 @@ function parseArgs(args) {
96
98
  yes: false,
97
99
  help: false,
98
100
  badArg: false,
101
+ version: false,
99
102
  only: null,
100
103
  except: null,
101
104
  targets: [],
@@ -111,6 +114,7 @@ function parseArgs(args) {
111
114
  const arg = args[i];
112
115
  if (arg === "-l" || arg === "--local") flags.local = true;
113
116
  else if (arg === "-y" || arg === "--yes") flags.yes = true;
117
+ else if (arg === "-V" || arg === "--version") flags.version = true;
114
118
  else if (arg === "-h" || arg === "--help") flags.help = true;
115
119
  else if (arg === "--only") flags.only = parseList(args[++i]);
116
120
  else if (arg.startsWith("--only=")) flags.only = parseList(arg.slice("--only=".length));
@@ -185,6 +189,7 @@ ${bold("Install options:")}
185
189
  -l, --local Install into the current project (.pi/settings.json)
186
190
  -y, --yes Skip the picker and any confirmation prompt
187
191
  -h, --help Show this help
192
+ -V, --version Show the installed version
188
193
 
189
194
  ${bold("Default behaviour:")}
190
195
  - Every catalog package is installed (lazy on purpose).
@@ -193,7 +198,7 @@ ${bold("Default behaviour:")}
193
198
  - With --yes, --only, or --except the picker is skipped.
194
199
 
195
200
  ${bold("Categories:")}
196
- core the harness control plane: sub-agents, ask gate, skill visibility, $ mention, goal, side chat, context budget, simplify, web research, fff search
201
+ core the harness control plane: sub-agents, workflows, ask gate, skill visibility, $ mention, goal, side chat, context budget, simplify, web research, fff search, ponytail discipline
197
202
  Non-catalog extras (pi install npm:<source>): skill-args, memory, mcp,
198
203
  interactive-shell, ralph-wiggum, curated-themes (65 dark themes)
199
204
 
@@ -210,6 +215,7 @@ ${bold("Examples:")}
210
215
  // ---------------------------------------------------------------------------
211
216
  // Pi / settings plumbing
212
217
  // ---------------------------------------------------------------------------
218
+ const VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
213
219
  // On Windows, package-manager CLIs and global Node bins are usually `.cmd`
214
220
  // shims. Node's child_process docs note that those need to be launched via a
215
221
  // shell, so we route spawned commands through the platform shell there while
@@ -326,8 +332,15 @@ function legacySourcesForPackage(pkg) {
326
332
  return Array.isArray(pkg.legacySources) ? pkg.legacySources : [];
327
333
  }
328
334
 
335
+ // An installed source matches a catalog source when equal or when it is the
336
+ // same source with a version/pin suffix (npm:pkg vs npm:pkg@x.y.z,
337
+ // git:... vs git:...@sha).
338
+ export function sourcesMatch(catalogSource, installedSource) {
339
+ return catalogSource === installedSource || installedSource.startsWith(`${catalogSource}@`);
340
+ }
341
+
329
342
  function isLegacySourceForPackage(pkg, source) {
330
- return legacySourcesForPackage(pkg).includes(source);
343
+ return legacySourcesForPackage(pkg).some((legacy) => sourcesMatch(legacy, source));
331
344
  }
332
345
 
333
346
  function findLegacyInstalledSources(pkg, installedPiSources) {
@@ -348,10 +361,11 @@ function removeLegacy(pkg, installedPiSources, local, interactive) {
348
361
 
349
362
  function packageInstallStatus(pkg, installedPiSources) {
350
363
  const legacySources = findLegacyInstalledSources(pkg, installedPiSources);
364
+ const installed = [...installedPiSources].some((src) => sourcesMatch(pkg.source, src));
351
365
  return {
352
- installed: installedPiSources.has(pkg.source),
366
+ installed,
353
367
  legacy: legacySources.length > 0,
354
- present: installedPiSources.has(pkg.source) || legacySources.length > 0,
368
+ present: installed || legacySources.length > 0,
355
369
  };
356
370
  }
357
371
 
@@ -690,11 +704,12 @@ function cmdStatus(flags) {
690
704
  return 1;
691
705
  }
692
706
 
693
- const piCatalogSources = new Set(PACKAGES.flatMap((p) => [p.source, ...legacySourcesForPackage(p)]));
694
707
  const installed = PACKAGES.filter((pkg) => packageInstallStatus(pkg, sources).installed);
695
708
  const legacy = PACKAGES.filter((pkg) => packageInstallStatus(pkg, sources).legacy);
696
709
  const missing = PACKAGES.filter((pkg) => !packageInstallStatus(pkg, sources).present);
697
- const others = [...sources].filter((src) => !piCatalogSources.has(src));
710
+ const others = [...sources].filter(
711
+ (src) => !PACKAGES.some((pkg) => sourcesMatch(pkg.source, src) || isLegacySourceForPackage(pkg, src)),
712
+ );
698
713
 
699
714
  printHeader(`Installed from LazyPi catalog (${installed.length}/${PACKAGES.length}):`);
700
715
  if (installed.length === 0) console.log(dim(" none"));
@@ -849,7 +864,7 @@ async function cmdRemove(flags, targets) {
849
864
 
850
865
  const sourcesToRemove = pkg
851
866
  ? [
852
- ...(installedSources.has(pkg.source) ? [pkg.source] : []),
867
+ ...[...installedSources].filter((src) => sourcesMatch(pkg.source, src)),
853
868
  ...findLegacyInstalledSources(pkg, installedSources),
854
869
  ]
855
870
  : [source];
@@ -872,6 +887,10 @@ async function cmdRemove(flags, targets) {
872
887
  // ---------------------------------------------------------------------------
873
888
  async function main() {
874
889
  const flags = parseArgs(argv.slice(2));
890
+ if (flags.version) {
891
+ console.log(`@tommy-ca/lazypi ${VERSION}`);
892
+ return 0;
893
+ }
875
894
  if (flags.help) {
876
895
  printHelp();
877
896
  return flags.badArg ? 2 : 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tommy-ca/lazypi",
3
- "version": "0.6.5",
3
+ "version": "0.8.0",
4
4
  "description": "Opinionated one-shot installer for a full-featured Pi coding agent setup.",
5
5
  "license": "MIT",
6
6
  "repository": {