prompt-profile 0.2.1 → 0.2.2

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.
Files changed (3) hide show
  1. package/README.md +3 -1
  2. package/dist/bin.js +93 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -59,7 +59,9 @@ for extraction and macOS setup.
59
59
  - **PNG share cards**: a 1200 × 630 card per language, without prompt quotes.
60
60
  - **`profile.json`**: the data used to render the report again without another analysis.
61
61
  - **Local history**: successful analyses are kept in a report library; open the output folder's
62
- `index.html` to browse them.
62
+ `index.html` to browse them. Generated output includes a `README.md` explaining each file,
63
+ backups, deletion and index repair; an existing user README is preserved. New report folders use readable local timestamps, for example
64
+ `2026-11-01_03-12-04+0800-<id>` (`+0800` is the UTC offset). Older report folders remain readable.
63
65
 
64
66
  Reports support English and Traditional Chinese; quotes stay in their original language.
65
67
  The default is English, or both languages on a Chinese system locale. Each extra language
package/dist/bin.js CHANGED
@@ -1493,7 +1493,7 @@ const ChunkObservations = Schema.Struct({
1493
1493
  })).pipe(Schema.withDecodingDefaultKey(Effect.succeed([])))
1494
1494
  });
1495
1495
  // src/domain/Library.ts
1496
- const RUN_ID = /^(?:\d{8}T\d{6}Z-[a-f0-9]{32}|legacy-[a-f0-9]{32})$/u;
1496
+ const RUN_ID = /^(?:(?:\d{8}T\d{6}Z|\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}[+-]\d{4})-[a-f0-9]{32}|legacy-[a-f0-9]{32})$/u;
1497
1497
  const Count = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
1498
1498
  const Iso = Schema.String.check(Schema.makeFilter((s) => /^\d{4}-\d\d-\d\dT/u.test(s) && Number.isFinite(Date.parse(s))));
1499
1499
  const RunManifest = Schema.Struct({
@@ -2288,6 +2288,83 @@ const readAsset = (name, locate) => new Uint8Array(readFileSync(locate()));
2288
2288
  * `$XDG_CACHE_HOME/prompt-profile`, else `~/.cache/prompt-profile` (macOS too).
2289
2289
  */
2290
2290
  const resolveCacheDir = (env, home, path) => path.join(env["XDG_CACHE_HOME"] || path.join(home, ".cache"), "prompt-profile");
2291
+ // src/platform/output-guide.ts
2292
+ const MARKER = "<!-- Generated by prompt-profile: output guide -->";
2293
+ const GUIDE = `${MARKER}
2294
+ # Your prompt-profile output
2295
+
2296
+ Open **index.html** to browse completed reports, newest first. Inside a report
2297
+ folder, open **report.html** directly. Both work offline without a server.
2298
+ Only files relevant to the commands you ran will be present.
2299
+
2300
+ ## What the files mean
2301
+
2302
+ | File or folder | Purpose |
2303
+ | --- | --- |
2304
+ | index.html | The report library: start here to find the latest report. |
2305
+ | reports/<timestamp>-<id>/ | One completed analysis. Each new analysis gets its own folder. |
2306
+ | reports/legacy-<id>/ | A preserved report from an older output layout. |
2307
+ | report.html | The readable report, with a language switch when available. |
2308
+ | card.png | The summary image in the report's first language. |
2309
+ | card.<language>.png | Additional language images, such as card.en.png. A missing card does not mean the HTML report failed. |
2310
+ | profile.json | The analysis data. Keep this to render supported profiles again without another LLM analysis. Older formats may require the package version that created them. |
2311
+ | run.json | Completion metadata: time, engine, languages, coverage and card status. Keep with the completed report. |
2312
+ | render.json | Rendering choices, version, profile fingerprint and relative source path. |
2313
+ | variants/<name>/ | Alternative renders. Each contains its own report, profile, rendering metadata and available cards. Rendering the same variant again replaces its generated files. |
2314
+ | library.json | Rebuildable catalog of completed reports; it is not the analysis itself. |
2315
+ | executions/<id>/summary.json | Execution status, call accounting and recovery information, including failed or interrupted runs. These are separate from completed reports. |
2316
+ | stats.json | Statistics-only output from the stats command; replaced the next time stats uses this folder. |
2317
+ | plan.json and chunks/ | Dry-run estimate and redacted text prepared for analysis. Use a separate output folder for each dry run; later runs may leave older chunks. |
2318
+ | README.md | This generated guide. Your own existing README is left untouched. |
2319
+
2320
+ ## Dates and names
2321
+
2322
+ New analysis folders use the computer's local time, for example
2323
+ 2026-11-01_03-12-04+0800-<id>: November 1, 2026 at 03:12:04, UTC+08:00.
2324
+ The final ID prevents collisions. Older names such as 20261031T191204Z-<id>
2325
+ use UTC (Z); they represent the same instant in this example. JSON timestamps
2326
+ remain in machine-readable formats. The library orders reports by completion
2327
+ time, so use index.html rather than sorting mixed old and new folder names.
2328
+
2329
+ ## Keep, move or delete
2330
+
2331
+ - Back up or move the whole output folder to preserve relative links. Copy a
2332
+ complete report folder when archiving one analysis; its link back to the library
2333
+ needs the original surrounding layout. Do not rename report folders or edit IDs.
2334
+ - To remove an analysis, delete its whole folder under reports/, including any
2335
+ variants you no longer need. This permanently removes that saved analysis.
2336
+ Never delete profile.json or run.json alone from a report you want to keep.
2337
+ - After deleting reports, rebuild index.html and library.json without an LLM call:
2338
+
2339
+ prompt-profile history --out "<output-folder>"
2340
+
2341
+ - Unwanted variants, stats.json, dry-run plan.json and chunks/ can be deleted
2342
+ independently. Keep profile.json if you want to render a variant again.
2343
+ - Execution folders can be deleted independently when their diagnostics are no
2344
+ longer needed. Deleting them does not remove completed reports or the separate
2345
+ analysis cache; keep them while troubleshooting an interrupted run.
2346
+ - Hidden reports/.pending-* folders are unfinished output. Remove them only after
2347
+ confirming that no prompt-profile process is using this output folder.
2348
+ - Older root-level report files and variants may have been copied to a legacy
2349
+ archive. Remove originals only after verifying that the archived copies match.
2350
+
2351
+ These files contain personal analysis and may include prompt excerpts, local paths
2352
+ or redacted input. Review the files you choose to share. Render and history commands
2353
+ work locally; neither requires another LLM analysis. No saved report history is
2354
+ pruned automatically.
2355
+ `;
2356
+ /** Refresh our generated guide without replacing user notes or following README symlinks. */
2357
+ const writeOutputGuide = (root) => Effect.gen(function* () {
2358
+ const fs = yield* FileSystem.FileSystem;
2359
+ const path = yield* Path.Path;
2360
+ const file = path.join(root, "README.md");
2361
+ const info = yield* fs.lstat(file).pipe(Effect.catchIf(isNotFound$1, () => Effect.succeed(null)));
2362
+ if (info !== null) {
2363
+ if (info.type !== "File" || info.size > 64000n) return;
2364
+ if (!(yield* fs.readFileString(file)).startsWith(`${MARKER}\n`)) return;
2365
+ }
2366
+ yield* writePrivateFile(fs, path, file, GUIDE);
2367
+ }).pipe(Effect.mapError(toFileError(root)));
2291
2368
  // src/platform/executions.ts
2292
2369
  const ID = /^[0-9a-f-]{36}$/u;
2293
2370
  /** `<root>/executions`, never a symlink. */
@@ -2331,6 +2408,7 @@ const createExecution = (root, startedAtMs) => Effect.gen(function* () {
2331
2408
  const fs = yield* FileSystem.FileSystem;
2332
2409
  const path = yield* Path.Path;
2333
2410
  const folder = yield* directory(root, true);
2411
+ yield* writeOutputGuide(root);
2334
2412
  const id = randomUUID();
2335
2413
  yield* fs.makeDirectory(path.join(folder, id), { mode: 448 });
2336
2414
  const initial = {
@@ -2422,7 +2500,12 @@ const reportsDirectory = (root, create) => Effect.gen(function* () {
2422
2500
  });
2423
2501
  const createStage = (root) => Effect.gen(function* () {
2424
2502
  const reports = yield* reportsDirectory(root, true);
2425
- const id = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/gu, "").replace(/\.\d{3}Z$/u, "Z")}-${randomUUID().replaceAll("-", "")}`;
2503
+ const now = /* @__PURE__ */ new Date();
2504
+ const pad = (value) => String(value).padStart(2, "0");
2505
+ const date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
2506
+ const time = `${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`;
2507
+ const offset = -now.getTimezoneOffset();
2508
+ const id = `${`${date}_${time}${`${offset >= 0 ? "+" : "-"}${pad(Math.floor(Math.abs(offset) / 60))}${pad(Math.abs(offset) % 60)}`}`}-${randomUUID().replaceAll("-", "")}`;
2426
2509
  const directory = (yield* Path.Path).join(reports, `.pending-${id}`);
2427
2510
  yield* (yield* FileSystem.FileSystem).makeDirectory(directory, { mode: 448 });
2428
2511
  return {
@@ -2461,6 +2544,7 @@ const scan = (root) => Effect.gen(function* () {
2461
2544
  const repair = (root, render) => Effect.gen(function* () {
2462
2545
  const path = yield* Path.Path;
2463
2546
  yield* reportsDirectory(root, true);
2547
+ yield* writeOutputGuide(root);
2464
2548
  for (let attempt = 0; attempt < 32; attempt++) {
2465
2549
  const index = yield* scan(root);
2466
2550
  yield* write(path.join(root, "library.json"), json(index));
@@ -7999,7 +8083,7 @@ const cardFileName = (lang, isDefault) => isDefault ? "card.png" : `card.${lang}
7999
8083
  const noCjkFonts = Effect.fail(new FontUnavailable({ reason: "no CJK font available" }));
8000
8084
  /**
8001
8085
  * Write `report.html`, `profile.json` and one card per language into `outDir` (overwriting
8002
- * only those files). A card failure never fails the report. Every render also writes
8086
+ * only generated files, including README.md). A card failure never fails the report. Every render also writes
8003
8087
  * `render.json`: its options and the profile it came from (`source`, relative to `outDir`).
8004
8088
  */
8005
8089
  const writeReport = (profile, outDir, options = DEFAULT_RENDER_OPTIONS, source = null, cjkFonts = noCjkFonts) => Effect.gen(function* () {
@@ -8057,6 +8141,7 @@ const writeReport = (profile, outDir, options = DEFAULT_RENDER_OPTIONS, source =
8057
8141
  error: "reason" in e ? e.reason : e.message
8058
8142
  }))));
8059
8143
  }
8144
+ yield* writeOutputGuide(outDir).pipe(Effect.mapError((e) => new Error(e.detail)));
8060
8145
  const first = cards[0];
8061
8146
  return {
8062
8147
  html,
@@ -14337,7 +14422,7 @@ const analyze = (input) => Effect.gen(function* () {
14337
14422
  * Package version. Injected by tsdown at build time; during development and tests
14338
14423
  * (tsx / vitest) it falls back to reading package.json.
14339
14424
  */
14340
- const VERSION = "0.2.1";
14425
+ const VERSION = "0.2.2";
14341
14426
  // src/cli/render-error.ts
14342
14427
  /** How to fix an InvalidArgument, per flag. */
14343
14428
  const ARGUMENT_HINTS = {
@@ -14578,6 +14663,7 @@ const writeDryRun = (p, outDir, context, voice = DEFAULT_VOICE) => Effect.gen(fu
14578
14663
  }))
14579
14664
  };
14580
14665
  yield* writeFileOrFail(path.join(outDir, "plan.json"), `${JSON.stringify(summary, null, 2)}\n`);
14666
+ yield* writeOutputGuide(outDir);
14581
14667
  });
14582
14668
  /**
14583
14669
  * The plan a CLI reports wins over the hint from local files; the file's extra detail is kept.
@@ -14954,8 +15040,10 @@ const runStats = (options) => Effect.gen(function* () {
14954
15040
  yield* out.line("");
14955
15041
  const collected = yield* readSources;
14956
15042
  const { stats } = yield* runPipelineStep(collected, options.since);
14957
- const file = path.join(yield* resolveOut(options.out), "stats.json");
15043
+ const root = yield* resolveOut(options.out);
15044
+ const file = path.join(root, "stats.json");
14958
15045
  yield* writeFileOrFail(file, `${JSON.stringify(encodeStats(stats), null, 2)}\n`);
15046
+ yield* writeOutputGuide(root);
14959
15047
  const peak = peakHour(stats);
14960
15048
  const percent = (x) => Math.round(x * 100);
14961
15049
  yield* out.line("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prompt-profile",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Read your Claude Code / Codex prompt history locally, analyse it with your logged-in claude or codex CLI, and generate a personal profile report.",
5
5
  "keywords": [
6
6
  "ai",