pm4ai 0.0.73

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.

Potentially problematic release.


This version of pm4ai might be problematic. Click here for more details.

Files changed (51) hide show
  1. package/README.md +34 -0
  2. package/dist/audit-oQQfgtxr.mjs +287 -0
  3. package/dist/cleanup-M-ALxTqh.mjs +35 -0
  4. package/dist/cli.d.mts +1 -0
  5. package/dist/cli.mjs +77 -0
  6. package/dist/dashboard-DVRNZGun.mjs +39 -0
  7. package/dist/discover-d8ENQC1K.mjs +172 -0
  8. package/dist/fix-BcMN_cuG.mjs +260 -0
  9. package/dist/fix-DvtItv_V.mjs +2 -0
  10. package/dist/guide-BS7-RqpH.d.mts +4 -0
  11. package/dist/guide-CifUmtQN.mjs +59 -0
  12. package/dist/guide.d.mts +2 -0
  13. package/dist/guide.mjs +2 -0
  14. package/dist/ignores-BBl55eUM.mjs +37 -0
  15. package/dist/index.d.mts +83 -0
  16. package/dist/index.mjs +11 -0
  17. package/dist/init-C-073mRX.mjs +120 -0
  18. package/dist/list-QdJPgkEO.mjs +31 -0
  19. package/dist/package-NpIViQjo.mjs +4 -0
  20. package/dist/schemas-Dsbtf6P2.mjs +51 -0
  21. package/dist/schemas.d.mts +48 -0
  22. package/dist/schemas.mjs +2 -0
  23. package/dist/setup-BPuE4oWT.mjs +164 -0
  24. package/dist/status-ByiuW1iF.mjs +2 -0
  25. package/dist/status-CzCNkG58.mjs +1775 -0
  26. package/dist/sync-DN1rgN3P.mjs +732 -0
  27. package/dist/templates/cli/package.json +30 -0
  28. package/dist/templates/cli/src/cli.ts +16 -0
  29. package/dist/templates/cli/src/index.ts +2 -0
  30. package/dist/templates/cli/src/tui.tsx +57 -0
  31. package/dist/templates/cli/tsdown.config.ts +9 -0
  32. package/dist/templates/docs/content/docs/index.mdx +6 -0
  33. package/dist/templates/docs/package.json +20 -0
  34. package/dist/templates/docs/source.config.ts +16 -0
  35. package/dist/templates/docs/src/app/(home)/page.tsx +11 -0
  36. package/dist/templates/lib/package.json +22 -0
  37. package/dist/templates/lib/src/index.ts +2 -0
  38. package/dist/templates/lib/tsdown.config.ts +9 -0
  39. package/dist/templates/root-package.txt +38 -0
  40. package/dist/templates/web/package.json +17 -0
  41. package/dist/templates/web/src/app/page.tsx +6 -0
  42. package/dist/templates/web/src/app/providers.tsx +15 -0
  43. package/dist/utils-CpkOMuQN.mjs +221 -0
  44. package/dist/watch-D4OSFClu.mjs +566 -0
  45. package/dist/watch-emitter-uTmZ3oQd.mjs +177 -0
  46. package/dist/watch-state-DIMHiLr5.d.mts +118 -0
  47. package/dist/watch-state-wF-NfC-k.mjs +274 -0
  48. package/dist/watch-state.d.mts +2 -0
  49. package/dist/watch-state.mjs +2 -0
  50. package/dist/watch-types-BzSNCGb2.mjs +22 -0
  51. package/package.json +65 -0
@@ -0,0 +1,260 @@
1
+ import { f as projectName, u as isInsideProject, x as CONFIG_DIR } from "./utils-CpkOMuQN.mjs";
2
+ import { r as lockSchema, s as safeParseJson } from "./schemas-Dsbtf6P2.mjs";
3
+ import { t as audit } from "./audit-oQQfgtxr.mjs";
4
+ import { c as writeCheckResult, n as emitToSocket } from "./watch-emitter-uTmZ3oQd.mjs";
5
+ import { n as discoverSources, t as discover } from "./discover-d8ENQC1K.mjs";
6
+ import { a as syncPackageJson, c as syncUi, i as syncFumadocsGithubUrl, n as syncConfigs, o as syncSubPackages, r as syncFumadocsBuild, s as syncTsconfig, t as syncClaudeMd } from "./sync-DN1rgN3P.mjs";
7
+ import { r as createEvent } from "./watch-types-BzSNCGb2.mjs";
8
+ import { $ } from "bun";
9
+ import { closeSync, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { homedir } from "node:os";
12
+ //#region src/log.ts
13
+ const logDir = join(homedir(), CONFIG_DIR, "logs");
14
+ const leadingSepRe = /^--/u;
15
+ const logPath = (path) => join(logDir, `${path.replaceAll("/", "--").replace(leadingSepRe, "")}.json`);
16
+ const updateLog = (entry) => {
17
+ mkdirSync(logDir, { recursive: true });
18
+ writeFileSync(logPath(entry.path), JSON.stringify(entry));
19
+ };
20
+ //#endregion
21
+ //#region src/fix.ts
22
+ const violationRe = /(?<count>\d+)\s*(?:error|violation|problem|issue)/iu;
23
+ const maintain = async (projectPath) => {
24
+ const issues = [];
25
+ if (!existsSync(join(projectPath, "up.sh"))) {
26
+ issues.push({
27
+ detail: "missing, cannot maintain",
28
+ type: "up.sh"
29
+ });
30
+ return issues;
31
+ }
32
+ const result = await $`sh up.sh`.cwd(projectPath).quiet().nothrow();
33
+ const { exitCode } = result;
34
+ const stderr = [result.stdout.toString(), result.stderr.toString()].join("\n").trim();
35
+ if (exitCode === 0) {
36
+ const snapshotDir = join(homedir(), CONFIG_DIR, "snapshots", projectName(projectPath));
37
+ const lockfile = join(projectPath, "bun.lock");
38
+ if (existsSync(lockfile)) {
39
+ mkdirSync(snapshotDir, { recursive: true });
40
+ copyFileSync(lockfile, join(snapshotDir, "bun.lock"));
41
+ }
42
+ writeCheckResult({
43
+ pass: true,
44
+ projectPath,
45
+ violations: 0
46
+ });
47
+ } else {
48
+ const errorLine = stderr.split("\n").findLast(Boolean) ?? "unknown error";
49
+ issues.push({
50
+ detail: `failed: ${errorLine}`,
51
+ type: "up.sh"
52
+ });
53
+ const violationMatch = violationRe.exec(stderr);
54
+ writeCheckResult({
55
+ pass: false,
56
+ projectPath,
57
+ summary: errorLine,
58
+ violations: violationMatch?.groups?.count ? Number.parseInt(violationMatch.groups.count, 10) : 1
59
+ });
60
+ }
61
+ updateLog({
62
+ at: (/* @__PURE__ */ new Date()).toISOString(),
63
+ error: exitCode === 0 ? void 0 : stderr.slice(0, 500),
64
+ pass: exitCode === 0,
65
+ path: projectPath,
66
+ project: projectName(projectPath)
67
+ });
68
+ return issues;
69
+ };
70
+ const fix = async (all = false, excludes = []) => {
71
+ const lockFile = join(homedir(), CONFIG_DIR, "fix.lock");
72
+ mkdirSync(join(homedir(), CONFIG_DIR), { recursive: true });
73
+ const lockData = JSON.stringify({
74
+ at: (/* @__PURE__ */ new Date()).toISOString(),
75
+ pid: process.pid
76
+ });
77
+ const tryAcquireLock = () => {
78
+ try {
79
+ const fd = openSync(lockFile, "wx");
80
+ writeFileSync(fd, lockData);
81
+ closeSync(fd);
82
+ return true;
83
+ } catch {
84
+ return false;
85
+ }
86
+ };
87
+ if (!tryAcquireLock()) {
88
+ try {
89
+ const lock = safeParseJson(lockSchema, readFileSync(lockFile, "utf8"));
90
+ if (!lock) return;
91
+ const age = Date.now() - new Date(lock.at).getTime();
92
+ let alive = false;
93
+ try {
94
+ process.kill(lock.pid, 0);
95
+ alive = true;
96
+ } catch {}
97
+ if (alive && age < 6e5) {
98
+ console.log("another fix is already running");
99
+ return;
100
+ }
101
+ } catch {}
102
+ rmSync(lockFile, { force: true });
103
+ if (!tryAcquireLock()) {
104
+ console.log("another fix is already running");
105
+ return;
106
+ }
107
+ }
108
+ try {
109
+ const resolveTargets = async () => {
110
+ if (all) return discover(void 0, excludes);
111
+ const projectPath = await isInsideProject();
112
+ if (projectPath) {
113
+ const { self, cnsync } = await discoverSources();
114
+ return {
115
+ cnsync,
116
+ consumers: [{
117
+ isCnsync: false,
118
+ isSelf: false,
119
+ name: projectName(projectPath),
120
+ path: projectPath
121
+ }],
122
+ self
123
+ };
124
+ }
125
+ return discover(void 0, excludes);
126
+ };
127
+ const { cnsync, consumers, self } = await resolveTargets();
128
+ console.log(`found ${consumers.length} projects`);
129
+ console.log();
130
+ const allRepos = [
131
+ self,
132
+ cnsync,
133
+ ...consumers
134
+ ];
135
+ const blocked = [];
136
+ const pullable = [];
137
+ const checkResults = await Promise.all(allRepos.map(async (repo) => {
138
+ const name = projectName(repo.path);
139
+ if ((await $`git status --porcelain`.cwd(repo.path).quiet().nothrow()).stdout.toString().trim()) return {
140
+ name,
141
+ reason: "uncommitted changes"
142
+ };
143
+ await $`git fetch`.cwd(repo.path).quiet().nothrow();
144
+ const behind = await $`git rev-list --count HEAD..@{u}`.cwd(repo.path).quiet().nothrow();
145
+ const ahead = await $`git rev-list --count @{u}..HEAD`.cwd(repo.path).quiet().nothrow();
146
+ const b = Number.parseInt(behind.stdout.toString().trim(), 10);
147
+ const a = Number.parseInt(ahead.stdout.toString().trim(), 10);
148
+ if (b > 0 && a > 0) return {
149
+ name,
150
+ reason: `diverged (${b} behind, ${a} ahead)`
151
+ };
152
+ if (a > 0) return {
153
+ name,
154
+ reason: `${a} commits ahead, push first`
155
+ };
156
+ return {
157
+ behind: b,
158
+ name,
159
+ path: repo.path
160
+ };
161
+ }));
162
+ for (const r of checkResults) if ("reason" in r) blocked.push(`${r.name}: ${r.reason}`);
163
+ else if (r.behind > 0) pullable.push({
164
+ name: r.name,
165
+ path: r.path
166
+ });
167
+ if (blocked.length > 0) {
168
+ console.log("fix requires clean git state:");
169
+ for (const msg of blocked) console.log(` ${msg}`);
170
+ return;
171
+ }
172
+ await Promise.all(pullable.map(async (repo) => {
173
+ await $`git pull`.cwd(repo.path).quiet().nothrow();
174
+ console.log(`${repo.name}: pulled`);
175
+ }));
176
+ const selfSubPkgIssues = await syncSubPackages(self.path, self.path);
177
+ if (selfSubPkgIssues.length > 0) for (const i of selfSubPkgIssues) console.log(` ${i.type} ${i.detail}`);
178
+ const allTargets = [cnsync, ...consumers];
179
+ const tasks = allTargets.map(async (project) => {
180
+ const name = projectName(project.path);
181
+ const issues = [];
182
+ emitToSocket(createEvent({
183
+ project: name,
184
+ status: "start",
185
+ step: "sync"
186
+ }));
187
+ const [configIssues, claudeIssues, pkgIssues, tsconfigIssues, fumadocsBuildIssues, fumadocsGithubIssues] = await Promise.all([
188
+ syncConfigs(self.path, project.path),
189
+ syncClaudeMd(self.path, project.path),
190
+ syncPackageJson(project.path, self.path),
191
+ syncTsconfig(project.path),
192
+ syncFumadocsBuild(project.path),
193
+ syncFumadocsGithubUrl(project.path)
194
+ ]);
195
+ const subPkgIssues = await syncSubPackages(self.path, project.path);
196
+ issues.push(...configIssues, ...claudeIssues, ...pkgIssues, ...tsconfigIssues, ...fumadocsBuildIssues, ...fumadocsGithubIssues, ...subPkgIssues);
197
+ if (existsSync(join(project.path, "readonly/ui"))) issues.push(...syncUi(cnsync.path, project.path));
198
+ const syncCount = issues.filter((i) => i.type === "synced").length;
199
+ emitToSocket(createEvent({
200
+ detail: syncCount > 0 ? `${syncCount} synced` : void 0,
201
+ project: name,
202
+ status: "ok",
203
+ step: "sync"
204
+ }));
205
+ emitToSocket(createEvent({
206
+ project: name,
207
+ status: "start",
208
+ step: "audit"
209
+ }));
210
+ const auditIssues = await audit(project.path);
211
+ issues.push(...auditIssues);
212
+ emitToSocket(createEvent({
213
+ detail: auditIssues.length > 0 ? `${auditIssues.length} issues` : void 0,
214
+ project: name,
215
+ status: auditIssues.length > 0 ? "fail" : "ok",
216
+ step: "audit"
217
+ }));
218
+ emitToSocket(createEvent({
219
+ project: name,
220
+ status: "start",
221
+ step: "maintain"
222
+ }));
223
+ const maintainIssues = await maintain(project.path);
224
+ issues.push(...maintainIssues);
225
+ const maintainDetail = maintainIssues[0]?.detail;
226
+ emitToSocket(createEvent({
227
+ detail: maintainDetail,
228
+ project: name,
229
+ status: maintainIssues.length > 0 ? "fail" : "ok",
230
+ step: "maintain"
231
+ }));
232
+ const changed = (await $`git status --porcelain`.cwd(project.path).quiet().nothrow()).stdout.toString().trim();
233
+ const fileCount = changed ? changed.split("\n").length : 0;
234
+ emitToSocket(createEvent({
235
+ detail: fileCount > 0 ? `${fileCount} files modified` : "clean",
236
+ project: name,
237
+ status: "ok",
238
+ step: "done"
239
+ }));
240
+ if (issues.length > 0) {
241
+ const lines = [project.path, ...issues.map((i) => ` ${i.type} ${i.detail}`)];
242
+ console.log(lines.join("\n"));
243
+ console.log();
244
+ }
245
+ });
246
+ await Promise.all(tasks);
247
+ console.log("--- changes ---");
248
+ const summaries = await Promise.all(allTargets.map(async (project) => {
249
+ const changed = (await $`git status --porcelain`.cwd(project.path).quiet().nothrow()).stdout.toString().trim();
250
+ const count = changed ? changed.split("\n").length : 0;
251
+ return count > 0 ? `${projectName(project.path)}: ${count} files modified` : `${projectName(project.path)}: clean`;
252
+ }));
253
+ for (const s of summaries) console.log(s);
254
+ if (process.platform === "darwin") await $`open swiftbar://refreshplugin?name=pm4ai`.quiet().nothrow();
255
+ } finally {
256
+ rmSync(lockFile, { force: true });
257
+ }
258
+ };
259
+ //#endregion
260
+ export { maintain as n, fix as t };
@@ -0,0 +1,2 @@
1
+ import { t as fix } from "./fix-BcMN_cuG.mjs";
2
+ export { fix };
@@ -0,0 +1,4 @@
1
+ //#region src/guide.d.ts
2
+ declare const guide = "pm4ai \u2014 agent-first anti-slop project management for TypeScript monorepos\ndiscovers projects by scanning for lintmax in package.json deps\ndiscovers itself and cnsync the same way (auto-clones if not found)\ncontext-aware: inside a project, operates on that project only\ncommands:\n pm4ai this guide\n pm4ai status check current project (or all if outside a project)\n pm4ai fix sync + maintain current project (requires clean git)\n pm4ai init <n> scaffold a new pm4ai-ready project\n pm4ai watch live terminal dashboard (connects to running fix/status)\n pm4ai ignores rank lint suppressions by frequency across projects\n pm4ai dashboard local web dashboard at http://localhost:4200\n pm4ai setup install swiftbar menubar plugin + launchd daily auto-run\nflags:\n --all force global scan across all projects\n --swiftbar output in SwiftBar menubar format\n --json (watch) output raw newline-delimited JSON events\n --verbose print debug info to stderr\nfix behavior:\n blocks if git is dirty or ahead (unpushed)\n pulls clean repos before syncing\n syncs: .github/workflows/ci.yml, clean.sh, up.sh, bunfig.toml, .gitignore, CLAUDE.md, readonly/ui\n maintains: runs sh up.sh (clean + install + build + fix + check)\n shows file change summary after completion\nstatus output (only issues shown, healthy projects omitted):\n /path/to/project\n git 3 uncommitted changes\n file clean.sh out of sync\n missing turbo.json\n drift clean should start with \"sh clean.sh\"\n dep react should be \"latest\" or \"^major\"\n duplicate ai already provided by workspace dep\n forbidden npm found, use bun only\n ci failed 2026-04-02\n deploy vercel deployment failed\nchecks:\n git status (auto-pulls clean repos)\n config drift (synced files match source)\n missing infra (turbo.json, tsconfig.json, ci.yml)\n root package.json (private, packageManager, hooks, sherif, prepare, clean)\n tsconfig extends lintmax/tsconfig, no include\n vercel.json installCommand is bun i\n vercel deployment status\n layout conventions (suppressHydrationWarning, antialiased, tracking, min-h-screen, font-sans, metadata, fonts.ts, providers.tsx, global.css, arrow function export, no RootLayout, no Provider inline)\n page conventions (arrow function export)\n next.config (reactStrictMode, no redundant postcss in apps)\n app tsconfig (extends lintmax, no include)\n banned packages (817 entries), bun globals, @a/ui deep imports\n deps on latest or ^major, no duplicates across workspaces\n no npm/yarn/pnpm in scripts or lockfiles\n turbo scripts have --output-logs=errors-only\n published packages have type:module, exports, files, license, repository\n workspace devDependencies hoisted to root\n no nested .gitignore, no postcss.config.mjs, no @ts-nocheck\n no bun.lock tracked in git, no redundant clean scripts\n ci status via github api";
3
+ //#endregion
4
+ export { guide as t };
@@ -0,0 +1,59 @@
1
+ //#region src/guide.ts
2
+ const guide = `pm4ai — agent-first anti-slop project management for TypeScript monorepos
3
+ discovers projects by scanning for lintmax in package.json deps
4
+ discovers itself and cnsync the same way (auto-clones if not found)
5
+ context-aware: inside a project, operates on that project only
6
+ commands:
7
+ pm4ai this guide
8
+ pm4ai status check current project (or all if outside a project)
9
+ pm4ai fix sync + maintain current project (requires clean git)
10
+ pm4ai init <n> scaffold a new pm4ai-ready project
11
+ pm4ai watch live terminal dashboard (connects to running fix/status)
12
+ pm4ai ignores rank lint suppressions by frequency across projects
13
+ pm4ai dashboard local web dashboard at http://localhost:4200
14
+ pm4ai setup install swiftbar menubar plugin + launchd daily auto-run
15
+ flags:
16
+ --all force global scan across all projects
17
+ --swiftbar output in SwiftBar menubar format
18
+ --json (watch) output raw newline-delimited JSON events
19
+ --verbose print debug info to stderr
20
+ fix behavior:
21
+ blocks if git is dirty or ahead (unpushed)
22
+ pulls clean repos before syncing
23
+ syncs: .github/workflows/ci.yml, clean.sh, up.sh, bunfig.toml, .gitignore, CLAUDE.md, readonly/ui
24
+ maintains: runs sh up.sh (clean + install + build + fix + check)
25
+ shows file change summary after completion
26
+ status output (only issues shown, healthy projects omitted):
27
+ /path/to/project
28
+ git 3 uncommitted changes
29
+ file clean.sh out of sync
30
+ missing turbo.json
31
+ drift clean should start with "sh clean.sh"
32
+ dep react should be "latest" or "^major"
33
+ duplicate ai already provided by workspace dep
34
+ forbidden npm found, use bun only
35
+ ci failed 2026-04-02
36
+ deploy vercel deployment failed
37
+ checks:
38
+ git status (auto-pulls clean repos)
39
+ config drift (synced files match source)
40
+ missing infra (turbo.json, tsconfig.json, ci.yml)
41
+ root package.json (private, packageManager, hooks, sherif, prepare, clean)
42
+ tsconfig extends lintmax/tsconfig, no include
43
+ vercel.json installCommand is bun i
44
+ vercel deployment status
45
+ layout conventions (suppressHydrationWarning, antialiased, tracking, min-h-screen, font-sans, metadata, fonts.ts, providers.tsx, global.css, arrow function export, no RootLayout, no Provider inline)
46
+ page conventions (arrow function export)
47
+ next.config (reactStrictMode, no redundant postcss in apps)
48
+ app tsconfig (extends lintmax, no include)
49
+ banned packages (817 entries), bun globals, @a/ui deep imports
50
+ deps on latest or ^major, no duplicates across workspaces
51
+ no npm/yarn/pnpm in scripts or lockfiles
52
+ turbo scripts have --output-logs=errors-only
53
+ published packages have type:module, exports, files, license, repository
54
+ workspace devDependencies hoisted to root
55
+ no nested .gitignore, no postcss.config.mjs, no @ts-nocheck
56
+ no bun.lock tracked in git, no redundant clean scripts
57
+ ci status via github api`;
58
+ //#endregion
59
+ export { guide as t };
@@ -0,0 +1,2 @@
1
+ import { t as guide } from "./guide-BS7-RqpH.mjs";
2
+ export { guide };
package/dist/guide.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { t as guide } from "./guide-CifUmtQN.mjs";
2
+ export { guide };
@@ -0,0 +1,37 @@
1
+ import { f as projectName, u as isInsideProject } from "./utils-CpkOMuQN.mjs";
2
+ import { t as discover } from "./discover-d8ENQC1K.mjs";
3
+ import { $ } from "bun";
4
+ //#region src/ignores.ts
5
+ /** biome-ignore-all lint/performance/noAwaitInLoops: sequential project scan */
6
+ const ignores = async (all = false) => {
7
+ let projects;
8
+ if (all) {
9
+ const { consumers, self, cnsync } = await discover();
10
+ projects = [
11
+ self,
12
+ cnsync,
13
+ ...consumers
14
+ ];
15
+ } else {
16
+ const projectPath = await isInsideProject();
17
+ if (projectPath) projects = [{
18
+ name: projectName(projectPath),
19
+ path: projectPath
20
+ }];
21
+ else {
22
+ const { consumers, self, cnsync } = await discover();
23
+ projects = [
24
+ self,
25
+ cnsync,
26
+ ...consumers
27
+ ];
28
+ }
29
+ }
30
+ for (const project of projects) {
31
+ console.log(`${project.name}:`);
32
+ await $`bunx lintmax@latest ignores`.cwd(project.path).nothrow();
33
+ console.log();
34
+ }
35
+ };
36
+ //#endregion
37
+ export { ignores };
@@ -0,0 +1,83 @@
1
+ import { t as guide } from "./guide-BS7-RqpH.mjs";
2
+ import { A as createEvent, C as timeAgo, D as WatchEvent, E as WATCH_STEPS, O as WatchStatus, S as tickProjects, T as WATCH_STATUSES, _ as progressDots, a as ProjectInfo, b as sortByStatus, c as RunAction, d as STEP_COUNT, f as STEP_LABELS, g as nextProjectState, h as formatTime, k as WatchStep, l as RunState, m as deriveStats, n as DerivedStats, o as ProjectState, p as createInitState, r as IDLE_FALLBACK, s as RESET_DELAY, t as DISPLAY_STEPS, u as STATUS_ORDER, v as runReducer, w as CreateEventArgs, x as sparkline, y as smoothBar } from "./watch-state-DIMHiLr5.mjs";
3
+
4
+ //#region src/types.d.ts
5
+ interface Issue {
6
+ detail: string;
7
+ type: IssueType;
8
+ }
9
+ type IssueType = 'bun' | 'check' | 'ci' | 'dep' | 'deploy' | 'drift' | 'duplicate' | 'error' | 'file' | 'forbidden' | 'git' | 'info' | 'lintmax' | 'missing' | 'synced' | 'unused' | 'up.sh';
10
+ interface PackageJson {
11
+ bin?: Record<string, string> | string;
12
+ dependencies?: Record<string, string>;
13
+ devDependencies?: Record<string, string>;
14
+ exports?: Record<string, Record<string, string> | string>;
15
+ files?: string[];
16
+ license?: string;
17
+ main?: string;
18
+ name?: string;
19
+ packageManager?: string;
20
+ peerDependencies?: Record<string, string>;
21
+ private?: boolean;
22
+ repository?: {
23
+ directory?: string;
24
+ type?: string;
25
+ url?: string;
26
+ };
27
+ scripts?: Record<string, string>;
28
+ 'simple-git-hooks'?: Record<string, string>;
29
+ trustedDependencies?: string[];
30
+ type?: string;
31
+ workspaces?: string[];
32
+ }
33
+ //#endregion
34
+ //#region src/audit.d.ts
35
+ declare const audit: (projectPath: string) => Promise<Issue[]>;
36
+ //#endregion
37
+ //#region src/check-cache.d.ts
38
+ interface CheckResult {
39
+ at: string;
40
+ commit: string;
41
+ pass: boolean;
42
+ summary?: string;
43
+ violations: number;
44
+ }
45
+ declare const readCheckResult: (projectPath: string) => CheckResult | undefined;
46
+ //#endregion
47
+ //#region src/constants.d.ts
48
+ declare const TSDOWN_BASE: {
49
+ clean: boolean;
50
+ deps: {
51
+ neverBundle: string[];
52
+ };
53
+ dts: boolean;
54
+ format: "esm";
55
+ outDir: string;
56
+ };
57
+ //#endregion
58
+ //#region src/discover.d.ts
59
+ interface Project {
60
+ isCnsync: boolean;
61
+ isSelf: boolean;
62
+ name: string;
63
+ path: string;
64
+ }
65
+ declare const discover: (searchRoot?: string, excludes?: readonly string[]) => Promise<{
66
+ cnsync: Project;
67
+ consumers: Project[];
68
+ self: Project;
69
+ }>;
70
+ //#endregion
71
+ //#region src/fix.d.ts
72
+ declare const fix: (all?: boolean, excludes?: readonly string[]) => Promise<void>;
73
+ //#endregion
74
+ //#region src/infer.d.ts
75
+ declare const inferRules: (projectPath: string, rulesDir?: string) => Promise<string[]>;
76
+ //#endregion
77
+ //#region src/status.d.ts
78
+ declare const status: (swiftbar?: boolean, all?: boolean, excludes?: readonly string[]) => Promise<void>;
79
+ //#endregion
80
+ //#region src/watch-emitter.d.ts
81
+ declare const SOCKET_PATH: string;
82
+ //#endregion
83
+ export { type CheckResult, type CreateEventArgs, DISPLAY_STEPS, type DerivedStats, IDLE_FALLBACK, type Issue, type IssueType, type PackageJson, type ProjectInfo, type ProjectState, RESET_DELAY, type RunAction, type RunState, SOCKET_PATH, STATUS_ORDER, STEP_COUNT, STEP_LABELS, WATCH_STATUSES, WATCH_STEPS, type WatchEvent, type WatchStatus, type WatchStep, audit, createEvent, createInitState, deriveStats, discover, fix, formatTime, guide, inferRules, nextProjectState, progressDots, readCheckResult, runReducer, smoothBar, sortByStatus, sparkline, status, tickProjects, timeAgo, TSDOWN_BASE as tsdownBase };
package/dist/index.mjs ADDED
@@ -0,0 +1,11 @@
1
+ import { L as TSDOWN_BASE } from "./utils-CpkOMuQN.mjs";
2
+ import { t as audit } from "./audit-oQQfgtxr.mjs";
3
+ import { o as readCheckResult, t as SOCKET_PATH } from "./watch-emitter-uTmZ3oQd.mjs";
4
+ import { t as discover } from "./discover-d8ENQC1K.mjs";
5
+ import { t as fix } from "./fix-BcMN_cuG.mjs";
6
+ import { l as inferRules } from "./sync-DN1rgN3P.mjs";
7
+ import { n as WATCH_STEPS, r as createEvent, t as WATCH_STATUSES } from "./watch-types-BzSNCGb2.mjs";
8
+ import { t as guide } from "./guide-CifUmtQN.mjs";
9
+ import { t as status } from "./status-CzCNkG58.mjs";
10
+ import { _ as tickProjects, a as STATUS_ORDER, c as createInitState, d as nextProjectState, f as progressDots, g as sparkline, h as sortByStatus, i as RESET_DELAY, l as deriveStats, m as smoothBar, n as IDLE_FALLBACK, o as STEP_COUNT, p as runReducer, s as STEP_LABELS, t as DISPLAY_STEPS, u as formatTime, v as timeAgo } from "./watch-state-wF-NfC-k.mjs";
11
+ export { DISPLAY_STEPS, IDLE_FALLBACK, RESET_DELAY, SOCKET_PATH, STATUS_ORDER, STEP_COUNT, STEP_LABELS, WATCH_STATUSES, WATCH_STEPS, audit, createEvent, createInitState, deriveStats, discover, fix, formatTime, guide, inferRules, nextProjectState, progressDots, readCheckResult, runReducer, smoothBar, sortByStatus, sparkline, status, tickProjects, timeAgo, TSDOWN_BASE as tsdownBase };
@@ -0,0 +1,120 @@
1
+ import { a as getBunVersion } from "./utils-CpkOMuQN.mjs";
2
+ import { t as discover } from "./discover-d8ENQC1K.mjs";
3
+ import { t as syncClaudeMd } from "./sync-DN1rgN3P.mjs";
4
+ import { $, file, write } from "bun";
5
+ import { cpSync, existsSync, mkdirSync, readdirSync, rmSync, statSync } from "node:fs";
6
+ import { basename, dirname, join, resolve } from "node:path";
7
+ import { homedir } from "node:os";
8
+ //#region src/init.ts
9
+ const SKIP = new Set([
10
+ ".git",
11
+ ".next",
12
+ ".turbo",
13
+ ".vercel",
14
+ "bun.lock",
15
+ "CLAUDE.md",
16
+ "dist",
17
+ "LEARNING.md",
18
+ "node_modules",
19
+ "PLAN.md",
20
+ "PROGRESS.md",
21
+ "prompts",
22
+ "README.md",
23
+ "RULES.md",
24
+ "vercel.json"
25
+ ]);
26
+ const REMOVE_PATHS = [
27
+ "apps/web/src/lib/router.ts",
28
+ "apps/web/src/lib/socket.ts",
29
+ "apps/web/src/lib/auth.ts",
30
+ "apps/web/src/lib/client.ts",
31
+ "apps/web/src/app/api",
32
+ "apps/web/src/app/auth",
33
+ "apps/web/src/tests",
34
+ "apps/docs/src/app/llms-full.txt",
35
+ "apps/docs/src/app/llms.txt",
36
+ "apps/docs/src/app/api"
37
+ ];
38
+ const copyTree = (src, dst, skipExtra) => {
39
+ for (const entry of readdirSync(src)) if (!(SKIP.has(entry) || skipExtra?.has(entry))) {
40
+ const s = join(src, entry);
41
+ const d = join(dst, entry);
42
+ if (statSync(s).isDirectory()) {
43
+ mkdirSync(d, { recursive: true });
44
+ copyTree(s, d);
45
+ } else cpSync(s, d);
46
+ }
47
+ };
48
+ const copyTemplateDir = async (tplDir, dstDir, projectName) => {
49
+ await Promise.all(readdirSync(tplDir).map(async (entry) => {
50
+ const s = join(tplDir, entry);
51
+ const d = join(dstDir, entry);
52
+ if (statSync(s).isDirectory()) {
53
+ mkdirSync(d, { recursive: true });
54
+ await copyTemplateDir(s, d, projectName);
55
+ } else {
56
+ const content = await file(s).text();
57
+ mkdirSync(dirname(d), { recursive: true });
58
+ await write(d, content.replaceAll("__NAME__", projectName));
59
+ }
60
+ }));
61
+ };
62
+ const patchFile = async (path, replacements) => {
63
+ if (!existsSync(path)) return;
64
+ let content = await file(path).text();
65
+ for (const [from, to] of replacements) content = content.replaceAll(from, to);
66
+ await write(path, content);
67
+ };
68
+ const templateDir = join(dirname(new URL(import.meta.url).pathname), "templates");
69
+ const init = async (name) => {
70
+ const projectName = basename(resolve(process.cwd(), name));
71
+ const dir = resolve(process.cwd(), name);
72
+ if (existsSync(dir)) {
73
+ console.log(`${dir} already exists`);
74
+ return;
75
+ }
76
+ const { self } = await discover();
77
+ const src = self.path;
78
+ console.log(`scaffolding ${projectName} from ${src}...`);
79
+ mkdirSync(dir, { recursive: true });
80
+ copyTree(src, dir, new Set(["packages"]));
81
+ for (const p of REMOVE_PATHS) rmSync(join(dir, p), {
82
+ force: true,
83
+ recursive: true
84
+ });
85
+ rmSync(join(dir, "packages"), {
86
+ force: true,
87
+ recursive: true
88
+ });
89
+ rmSync(join(dir, "apps", "docs", "content", "rules"), {
90
+ force: true,
91
+ recursive: true
92
+ });
93
+ mkdirSync(join(dir, "apps", "docs", "content", "docs"), { recursive: true });
94
+ await Promise.all([
95
+ copyTemplateDir(join(templateDir, "cli"), join(dir, "packages", "cli"), projectName),
96
+ copyTemplateDir(join(templateDir, "lib"), join(dir, "packages", "lib"), projectName),
97
+ copyTemplateDir(join(templateDir, "web"), join(dir, "apps", "web"), projectName),
98
+ copyTemplateDir(join(templateDir, "docs"), join(dir, "apps", "docs"), projectName)
99
+ ]);
100
+ const bunVersion = await getBunVersion();
101
+ const rootPkgText = (await file(join(templateDir, "root-package.txt")).text()).replace("__PACKAGE_MANAGER__", `bun@${bunVersion}`).replace("__NAME__", `${projectName}-workspace`);
102
+ await Promise.all([
103
+ write(join(dir, "package.json"), rootPkgText),
104
+ patchFile(join(dir, "apps", "docs", "src", "lib", "shared.ts"), [["pm4ai", projectName]]),
105
+ patchFile(join(dir, "apps", "docs", "src", "lib", "layout.shared.tsx"), [["pm4ai", projectName]]),
106
+ patchFile(join(dir, "apps", "web", "src", "app", "layout.tsx"), [["pm4ai dashboard", projectName]])
107
+ ]);
108
+ await syncClaudeMd(src, dir);
109
+ const bookInstall = join(homedir(), "book", "install.sh");
110
+ if (existsSync(bookInstall)) await $`sh ${bookInstall} ${dir}`.quiet().nothrow();
111
+ await $`git init`.cwd(dir).quiet();
112
+ await $`git add -A`.cwd(dir).quiet();
113
+ await $`git -c user.name=pm4ai -c user.email=pm4ai commit -m "init: scaffold from pm4ai"`.cwd(dir).quiet().nothrow();
114
+ console.log(`\ncreated ${projectName}/`);
115
+ console.log(`\n cd ${projectName}`);
116
+ console.log(" bun i");
117
+ console.log(" bun dev");
118
+ };
119
+ //#endregion
120
+ export { init };
@@ -0,0 +1,31 @@
1
+ import { t as discover } from "./discover-d8ENQC1K.mjs";
2
+ import { $ } from "bun";
3
+ //#region src/list.ts
4
+ const AHEAD_RE = /\+(?<n>\d+)/u;
5
+ const BEHIND_RE = /-(?<n>\d+)/u;
6
+ const gitStatus = async (path) => {
7
+ const out = (await $`git -C ${path} status -sb --porcelain=v2 --branch`.quiet().nothrow()).stdout.toString();
8
+ const aheadMatch = AHEAD_RE.exec(out);
9
+ const behindMatch = BEHIND_RE.exec(out);
10
+ const dirty = out.split("\n").some((l) => l.startsWith("1 ") || l.startsWith("2 ") || l.startsWith("?"));
11
+ return {
12
+ ahead: aheadMatch ? Number(aheadMatch.groups?.n) : 0,
13
+ behind: behindMatch ? Number(behindMatch.groups?.n) : 0,
14
+ dirty
15
+ };
16
+ };
17
+ const list = async (excludes = [], searchRoot, write = (row) => console.log(row)) => {
18
+ const { consumers } = await discover(searchRoot, excludes);
19
+ const rows = await Promise.all(consumers.map(async (c) => {
20
+ const g = await gitStatus(c.path);
21
+ const flags = [
22
+ g.dirty ? "dirty" : "clean",
23
+ g.behind ? `behind:${g.behind}` : "",
24
+ g.ahead ? `ahead:${g.ahead}` : ""
25
+ ].filter(Boolean).join(",");
26
+ return `${c.path}\t${flags}`;
27
+ }));
28
+ for (const r of rows.toSorted()) write(r);
29
+ };
30
+ //#endregion
31
+ export { list };
@@ -0,0 +1,4 @@
1
+ //#region package.json
2
+ var version = "0.0.73";
3
+ //#endregion
4
+ export { version as t };