prompt-profile 0.2.1 → 0.2.3
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 +18 -10
- package/dist/bin.js +119 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,13 +6,19 @@ collaborate with AI. Uses your logged-in `claude` or `codex` CLI: no API key nee
|
|
|
6
6
|
**Analysis sends selected, redacted prompts to your CLI's model service and uses your
|
|
7
7
|
subscription quota.** Reports and analysis caches stay on your computer.
|
|
8
8
|
|
|
9
|
-

|
|
10
10
|
|
|
11
|
-
[
|
|
12
|
-
· [
|
|
11
|
+
[My report](https://erictsai83.github.io/prompt-profile/owner-example/report.html)
|
|
12
|
+
· [繁體中文卡片](https://erictsai83.github.io/prompt-profile/owner-example/card.png)
|
|
13
|
+
· [Fictional example](https://erictsai83.github.io/prompt-profile/sample-report.html)
|
|
13
14
|
· [Template gallery](https://erictsai83.github.io/prompt-profile/demo/templates/index.html)
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
The card above is my personal analysis from November 1, 2026, rendered with the current
|
|
17
|
+
code. The public report omits prompt quotes, project names and custom instruction names.
|
|
18
|
+
Ability scores describe supported practice in my conversations, not population ranks;
|
|
19
|
+
the API estimate is not my subscription bill. The separate fictional examples and gallery
|
|
20
|
+
are generated from the latest repository code.
|
|
21
|
+
[View the latest README on GitHub](https://github.com/EricTsai83/prompt-profile#readme).
|
|
16
22
|
|
|
17
23
|
## Quick start
|
|
18
24
|
|
|
@@ -59,7 +65,9 @@ for extraction and macOS setup.
|
|
|
59
65
|
- **PNG share cards**: a 1200 × 630 card per language, without prompt quotes.
|
|
60
66
|
- **`profile.json`**: the data used to render the report again without another analysis.
|
|
61
67
|
- **Local history**: successful analyses are kept in a report library; open the output folder's
|
|
62
|
-
`index.html` to browse them.
|
|
68
|
+
`index.html` to browse them. Generated output includes a `README.md` explaining each file,
|
|
69
|
+
backups, deletion and index repair; an existing user README is preserved. New report folders use readable local timestamps, for example
|
|
70
|
+
`2026-11-01_03-12-04+0800-<id>` (`+0800` is the UTC offset). Older report folders remain readable.
|
|
63
71
|
|
|
64
72
|
Reports support English and Traditional Chinese; quotes stay in their original language.
|
|
65
73
|
The default is English, or both languages on a Chinese system locale. Each extra language
|
|
@@ -68,9 +76,9 @@ A Chinese-only request stops if translation still fails; it does not substitute
|
|
|
68
76
|
|
|
69
77
|
Observations cite verified prompt quotes, but quote matching and model review do not prove an
|
|
70
78
|
interpretation is correct. Small histories may show “Early observations” instead of a role.
|
|
71
|
-
The [radar](docs/ability-scores.md) assesses six AI collaboration abilities from verified quotes in these conversations. Scores describe supported practice, not psychological traits or population ranks; “not assessed” is not a low score.
|
|
79
|
+
The [radar](https://github.com/EricTsai83/prompt-profile/blob/main/docs/ability-scores.md) assesses six AI collaboration abilities from verified quotes in these conversations. Scores describe supported practice, not psychological traits or population ranks; “not assessed” is not a low score.
|
|
72
80
|
|
|
73
|
-
New analyses also retain [reusable instruction provenance](docs/instruction-provenance.md): what a skill is documented to do, who selected it for the task, and which historical requirements were supplied.
|
|
81
|
+
New analyses also retain [reusable instruction provenance](https://github.com/EricTsai83/prompt-profile/blob/main/docs/instruction-provenance.md): what a skill is documented to do, who selected it for the task, and which historical requirements were supplied.
|
|
74
82
|
|
|
75
83
|
## Usage
|
|
76
84
|
|
|
@@ -99,7 +107,7 @@ printed retry command: completed chunks and merges are cached; later steps may u
|
|
|
99
107
|
|
|
100
108
|
Each analysis saves calls, retries, cache reuse, token coverage and cost provenance, including
|
|
101
109
|
failed executions. The report shows generation usage separately from historical activity.
|
|
102
|
-
See [execution accounting](docs/execution-accounting.md) for recovery, unknown measurements
|
|
110
|
+
See [execution accounting](https://github.com/EricTsai83/prompt-profile/blob/main/docs/execution-accounting.md) for recovery, unknown measurements
|
|
103
111
|
and cost-threshold limits.
|
|
104
112
|
|
|
105
113
|
## How much a run uses
|
|
@@ -207,8 +215,8 @@ source prompts can be in any language.
|
|
|
207
215
|
|
|
208
216
|
## Development
|
|
209
217
|
|
|
210
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, checks and releases, and
|
|
211
|
-
[AGENTS.md](AGENTS.md) for architecture and repository rules.
|
|
218
|
+
See [CONTRIBUTING.md](https://github.com/EricTsai83/prompt-profile/blob/main/CONTRIBUTING.md) for setup, checks and releases, and
|
|
219
|
+
[AGENTS.md](https://github.com/EricTsai83/prompt-profile/blob/main/AGENTS.md) for architecture and repository rules.
|
|
212
220
|
|
|
213
221
|
## License
|
|
214
222
|
|
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
|
|
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));
|
|
@@ -4402,7 +4486,7 @@ const productDark = (bg) => ({
|
|
|
4402
4486
|
});
|
|
4403
4487
|
const CLASSIC = {
|
|
4404
4488
|
id: "classic",
|
|
4405
|
-
version:
|
|
4489
|
+
version: 37,
|
|
4406
4490
|
color: {
|
|
4407
4491
|
light: {
|
|
4408
4492
|
bg: "#f6f6f3",
|
|
@@ -5079,7 +5163,7 @@ const fontsOf = (theme) => {
|
|
|
5079
5163
|
/** Color tokens of a classic or product card. */
|
|
5080
5164
|
const schemeOf = (theme) => theme.color[theme.card.scheme];
|
|
5081
5165
|
/** The archetype name, headline and trait chips of a classic or identity card. */
|
|
5082
|
-
const identityCopy = (profile, theme, hide, lang, showGraphic, colors, badge = null, width) => {
|
|
5166
|
+
const identityCopy = (profile, theme, hide, lang, showGraphic, colors, badge = null, width, period = null) => {
|
|
5083
5167
|
const c = contentIn(profile, lang);
|
|
5084
5168
|
const card = theme.card;
|
|
5085
5169
|
const font = fontsOf(theme);
|
|
@@ -5112,6 +5196,12 @@ const identityCopy = (profile, theme, hide, lang, showGraphic, colors, badge = n
|
|
|
5112
5196
|
marginTop: card.headline.gap,
|
|
5113
5197
|
color: colors.muted
|
|
5114
5198
|
}, clip(c.headline, 120)),
|
|
5199
|
+
period === null ? null : h("div", {
|
|
5200
|
+
fontSize: card.size.header,
|
|
5201
|
+
color: colors.muted,
|
|
5202
|
+
marginTop: card.headline.gap,
|
|
5203
|
+
flexShrink: 0
|
|
5204
|
+
}, period),
|
|
5115
5205
|
hide.includes("traits") ? null : h("div", {
|
|
5116
5206
|
display: "flex",
|
|
5117
5207
|
gap: card.chips.gap,
|
|
@@ -5217,7 +5307,6 @@ const CLASSIC_RADAR = {
|
|
|
5217
5307
|
*/
|
|
5218
5308
|
const classicTree = (profile, theme, hide, lang, showUsage, exactDates, avatar) => {
|
|
5219
5309
|
const k = schemeOf(theme);
|
|
5220
|
-
const card = theme.card;
|
|
5221
5310
|
const showGraphic = !hide.includes("graphic");
|
|
5222
5311
|
return h("div", {
|
|
5223
5312
|
width: CARD_WIDTH,
|
|
@@ -5234,31 +5323,24 @@ const classicTree = (profile, theme, hide, lang, showUsage, exactDates, avatar)
|
|
|
5234
5323
|
muted: k.muted,
|
|
5235
5324
|
value: k.accent,
|
|
5236
5325
|
card: k.card
|
|
5237
|
-
}, !hide.includes("traits")) : [], copyLayer(theme, [
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
color: k.
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
}) : null, showGraphic ? 520 : void 0),
|
|
5256
|
-
h("div", {
|
|
5257
|
-
display: "flex",
|
|
5258
|
-
flexDirection: "column",
|
|
5259
|
-
gap: 18
|
|
5260
|
-
}, [metricRow(profile, theme, hide, lang, k.muted, showUsage), cardFooter(profile, theme, hide, lang, k.muted, showUsage, exactDates)])
|
|
5261
|
-
])]);
|
|
5326
|
+
}, !hide.includes("traits")) : [], copyLayer(theme, [h("div", {
|
|
5327
|
+
display: "flex",
|
|
5328
|
+
flex: 1,
|
|
5329
|
+
alignItems: "center"
|
|
5330
|
+
}, [identityCopy(profile, theme, showGraphic ? [...hide, "traits"] : hide, lang, showGraphic, {
|
|
5331
|
+
muted: k.muted,
|
|
5332
|
+
chip: {
|
|
5333
|
+
color: k.accent,
|
|
5334
|
+
backgroundColor: k.accentSoft
|
|
5335
|
+
}
|
|
5336
|
+
}, avatar && showGraphic ? roleBadge(avatar, lang, {
|
|
5337
|
+
fg: k.fg,
|
|
5338
|
+
border: k.border
|
|
5339
|
+
}) : null, showGraphic ? 520 : void 0, hide.includes("stats") ? null : cardPeriod(profile, lang, exactDates))]), h("div", {
|
|
5340
|
+
display: "flex",
|
|
5341
|
+
flexDirection: "column",
|
|
5342
|
+
gap: 18
|
|
5343
|
+
}, [metricRow(profile, theme, hide, lang, k.muted, showUsage), cardFooter(profile, theme, hide, lang, k.muted, showUsage, exactDates)])])]);
|
|
5262
5344
|
};
|
|
5263
5345
|
/** A share-first look: identity colors, display type and the activity graphic. */
|
|
5264
5346
|
const identityTree = (profile, theme, hide, lang, showUsage, exactDates, avatar) => {
|
|
@@ -7999,7 +8081,7 @@ const cardFileName = (lang, isDefault) => isDefault ? "card.png" : `card.${lang}
|
|
|
7999
8081
|
const noCjkFonts = Effect.fail(new FontUnavailable({ reason: "no CJK font available" }));
|
|
8000
8082
|
/**
|
|
8001
8083
|
* Write `report.html`, `profile.json` and one card per language into `outDir` (overwriting
|
|
8002
|
-
* only
|
|
8084
|
+
* only generated files, including README.md). A card failure never fails the report. Every render also writes
|
|
8003
8085
|
* `render.json`: its options and the profile it came from (`source`, relative to `outDir`).
|
|
8004
8086
|
*/
|
|
8005
8087
|
const writeReport = (profile, outDir, options = DEFAULT_RENDER_OPTIONS, source = null, cjkFonts = noCjkFonts) => Effect.gen(function* () {
|
|
@@ -8057,6 +8139,7 @@ const writeReport = (profile, outDir, options = DEFAULT_RENDER_OPTIONS, source =
|
|
|
8057
8139
|
error: "reason" in e ? e.reason : e.message
|
|
8058
8140
|
}))));
|
|
8059
8141
|
}
|
|
8142
|
+
yield* writeOutputGuide(outDir).pipe(Effect.mapError((e) => new Error(e.detail)));
|
|
8060
8143
|
const first = cards[0];
|
|
8061
8144
|
return {
|
|
8062
8145
|
html,
|
|
@@ -14337,7 +14420,7 @@ const analyze = (input) => Effect.gen(function* () {
|
|
|
14337
14420
|
* Package version. Injected by tsdown at build time; during development and tests
|
|
14338
14421
|
* (tsx / vitest) it falls back to reading package.json.
|
|
14339
14422
|
*/
|
|
14340
|
-
const VERSION = "0.2.
|
|
14423
|
+
const VERSION = "0.2.3";
|
|
14341
14424
|
// src/cli/render-error.ts
|
|
14342
14425
|
/** How to fix an InvalidArgument, per flag. */
|
|
14343
14426
|
const ARGUMENT_HINTS = {
|
|
@@ -14578,6 +14661,7 @@ const writeDryRun = (p, outDir, context, voice = DEFAULT_VOICE) => Effect.gen(fu
|
|
|
14578
14661
|
}))
|
|
14579
14662
|
};
|
|
14580
14663
|
yield* writeFileOrFail(path.join(outDir, "plan.json"), `${JSON.stringify(summary, null, 2)}\n`);
|
|
14664
|
+
yield* writeOutputGuide(outDir);
|
|
14581
14665
|
});
|
|
14582
14666
|
/**
|
|
14583
14667
|
* The plan a CLI reports wins over the hint from local files; the file's extra detail is kept.
|
|
@@ -14954,8 +15038,10 @@ const runStats = (options) => Effect.gen(function* () {
|
|
|
14954
15038
|
yield* out.line("");
|
|
14955
15039
|
const collected = yield* readSources;
|
|
14956
15040
|
const { stats } = yield* runPipelineStep(collected, options.since);
|
|
14957
|
-
const
|
|
15041
|
+
const root = yield* resolveOut(options.out);
|
|
15042
|
+
const file = path.join(root, "stats.json");
|
|
14958
15043
|
yield* writeFileOrFail(file, `${JSON.stringify(encodeStats(stats), null, 2)}\n`);
|
|
15044
|
+
yield* writeOutputGuide(root);
|
|
14959
15045
|
const peak = peakHour(stats);
|
|
14960
15046
|
const percent = (x) => Math.round(x * 100);
|
|
14961
15047
|
yield* out.line("");
|
package/package.json
CHANGED