Repository navigation
fix(ops): operator export scripts honor --help and refuse out-of-repo output paths #36
Changes from 3 commits
a568523
9f099aa
66bb043
aa3d690
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,9 @@ | |
| import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; | ||
| import { execFileSync } from "node:child_process"; | ||
| import { localIsoDate } from "./date-utils.mjs"; | ||
| import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs"; | ||
|
|
||
| handleHelp(process.argv.slice(2), `Usage: npm run growth:cockpit -- [--output=runs/growth-cockpit.html]`); | ||
| const outputArg = process.argv.find((arg) => arg.startsWith("--output=")); | ||
| const outputPath = outputArg ? outputArg.split("=")[1] : "runs/growth-cockpit.html"; | ||
| const today = localIsoDate(); | ||
|
|
@@ -241,9 +243,10 @@ const html = `<!doctype html> | |
| </html> | ||
| `; | ||
|
|
||
| const outputDir = outputPath.split("/").slice(0, -1).join("/"); | ||
| const resolvedOutputPath = resolveOutputPath(outputPath); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When AGENTS.md reference: AGENTS.md:L2-L3 Useful? React with 👍 / 👎. |
||
| const outputDir = resolvedOutputPath.split("/").slice(0, -1).join("/"); | ||
| if (outputDir) mkdirSync(outputDir, { recursive: true }); | ||
| writeFileSync(outputPath, `${html.replace(/[ \t]+$/gm, "").trimEnd()}\n`); | ||
| writeFileSync(resolvedOutputPath, `${html.replace(/[ \t]+$/gm, "").trimEnd()}\n`); | ||
|
Comment on lines
+246
to
+249
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- file location and size ---'
fd -t f 'export-growth-cockpit\.mjs$' .
wc -l scripts/export-growth-cockpit.mjs
printf '%s\n' '--- imports and path handling ---'
sed -n '1,45p' scripts/export-growth-cockpit.mjs
sed -n '220,260p' scripts/export-growth-cockpit.mjs
printf '%s\n' '--- relevant symbol usages ---'
rg -n 'resolveOutputPath|outputPath|mkdirSync|writeFileSync|dirname|pathToFileURL' scripts/export-growth-cockpit.mjsRepository: nish3451/tinystudio-in Length of output: 3568 🏁 Script executed: #!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path, PurePosixPath, PureWindowsPath
source = Path("scripts/export-growth-cockpit.mjs").read_text()
print("--- deterministic split behavior ---")
for value in [
r"C:\reports\growth.html",
r"C:/reports/growth.html",
"/reports/growth.html",
"growth.html",
]:
split_parent = value.split("/")[:-1]
split_dir = "/".join(split_parent)
print(f"{value!r} -> split parent {split_dir!r}")
print("--- Windows path interpretation ---")
for value in [r"C:\reports\growth.html", r"C:/reports/growth.html"]:
p = PureWindowsPath(value)
print(f"{value!r} -> parent {str(p.parent)!r}, name {p.name!r}")
PYRepository: nish3451/tinystudio-in Length of output: 556 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- operator CLI path resolver ---'
fd -t f 'operator-cli\.mjs$' .
wc -l scripts/lib/operator-cli.mjs
cat -n scripts/lib/operator-cli.mjs
printf '%s\n' '--- related output-path tests and documentation ---'
rg -n -i 'resolveOutputPath|--output=|growth-cockpit|output path' scripts test tests README.md package.json 2>/dev/null || trueRepository: nish3451/tinystudio-in Length of output: 27332 🏁 Script executed: #!/bin/bash
set -e
node - <<'JS'
const path = require("node:path");
const root = "C:\\service";
const inputs = [
"runs\\growth-cockpit.html",
"nested\\new\\growth-cockpit.html",
"runs/growth-cockpit.html",
"C:\\service\\nested\\new\\growth-cockpit.html",
];
for (const raw of inputs) {
const resolved = path.win32.resolve(root, raw);
const currentDir = resolved.split("/").slice(0, -1).join("/");
console.log(JSON.stringify({
raw,
resolved,
currentDir,
currentCreatesDirectory: Boolean(currentDir),
dirname: path.win32.dirname(resolved),
}));
}
JSRepository: nish3451/tinystudio-in Length of output: 878 Use On Windows, Use 🤖 Prompt for AI Agents |
||
|
|
||
| console.log(JSON.stringify({ | ||
| status: "created", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,9 @@ import { localIsoDate } from "./date-utils.mjs"; | |
| import { checkProspectReadiness, prospectWarningWeight } from "./lib/prospect-readiness.mjs"; | ||
| import { listOutboundProspectFolders } from "./lib/outbound-prospects.mjs"; | ||
| import { runRepoJson as runJson } from "./lib/runtime-roots.mjs"; | ||
| import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs"; | ||
|
|
||
| handleHelp(process.argv.slice(2), `Usage: npm run growth:doctor -- [--no-checks] [--plain] [--output=runs/growth-doctor.md]`); | ||
| const outputArg = process.argv.find((arg) => arg.startsWith("--output=")); | ||
| const outputPath = outputArg ? outputArg.split("=")[1] : "runs/growth-doctor.md"; | ||
| const skipChecks = process.argv.includes("--no-checks"); | ||
|
|
@@ -233,9 +235,10 @@ ${focusRows || "- No active focus items."} | |
| Do not build more system surface until the current bottleneck is worked. | ||
| `; | ||
|
|
||
| const outputDir = outputPath.split("/").slice(0, -1).join("/"); | ||
| const resolvedOutputPath = resolveOutputPath(outputPath); | ||
| const outputDir = resolvedOutputPath.split("/").slice(0, -1).join("/"); | ||
| if (outputDir) mkdirSync(outputDir, { recursive: true }); | ||
| writeFileSync(outputPath, markdown); | ||
| writeFileSync(resolvedOutputPath, markdown); | ||
|
Comment on lines
+238
to
+241
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -e
file="scripts/export-growth-doctor.mjs"
printf '%s\n' "== imports and target code =="
sed -n '1,45p' "$file"
sed -n '220,250p' "$file"
printf '%s\n' "== path-related usage =="
rg -n "resolveOutputPath|dirname|outputDir|mkdirSync|writeFileSync" "$file"Repository: nish3451/tinystudio-in Length of output: 3462 🏁 Script executed: #!/bin/bash
set -e
file="scripts/lib/operator-cli.mjs"
printf '%s\n' "== resolver definition =="
rg -n -A35 -B10 "function resolveOutputPath|const resolveOutputPath|export .*resolveOutputPath" "$file"
printf '%s\n' "== resolver callers =="
rg -n "resolveOutputPath\(" scripts
printf '%s\n' "== path module usage =="
rg -n "from \"node:path\"|from 'node:path'|win32|posix|normalize|resolve" scripts/lib/operator-cli.mjs scripts/export-growth-doctor.mjs
printf '%s\n' "== tracked related tests/docs =="
rg -n -i "growth-doctor|resolveOutputPath|output=.*runs|output path" --glob '!node_modules' .Repository: nish3451/tinystudio-in Length of output: 16993 🏁 Script executed: node - <<'JS'
import path from "node:path";
for (const [label, resolver] of [
["posix", path.posix.resolve],
["win32", path.win32.resolve]
]) {
const resolved = resolver("service-root", "runs/growth-doctor.md");
const current = resolved.split("/").slice(0, -1).join("/");
const correct = path[label].dirname(resolved);
console.log(JSON.stringify({ label, resolved, current, correct }));
}
JSRepository: nish3451/tinystudio-in Length of output: 501 Use On Windows, Import 🤖 Prompt for AI Agents |
||
|
|
||
| const result = { | ||
| status, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,9 @@ import { isValidLoomUrl } from "./lib/loom-url.mjs"; | |
| import { loadValidatedServiceClients } from "./lib/validated-service-client.mjs"; | ||
| import { listOutboundProspectFolders } from "./lib/outbound-prospects.mjs"; | ||
| import { runRepoJson } from "./lib/runtime-roots.mjs"; | ||
| import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs"; | ||
|
|
||
| handleHelp(process.argv.slice(2), `Usage: npm run growth:metrics -- [--plain] [--output=growth-brain/ops/live-metrics.md]`); | ||
| const outputArg = process.argv.find((arg) => arg.startsWith("--output=")); | ||
| const outputPath = outputArg ? outputArg.split("=")[1] : "growth-brain/ops/live-metrics.md"; | ||
| const plain = process.argv.includes("--plain"); | ||
|
|
@@ -182,9 +184,10 @@ ${Object.entries(stageCounts).sort(([a], [b]) => a.localeCompare(b)).map(([stage | |
| - If calls are high and closed is low, improve scope, price, or proof. | ||
| `; | ||
|
|
||
| const outputDir = outputPath.split("/").slice(0, -1).join("/"); | ||
| const resolvedOutputPath = resolveOutputPath(outputPath); | ||
| const outputDir = resolvedOutputPath.split("/").slice(0, -1).join("/"); | ||
| if (outputDir) mkdirSync(outputDir, { recursive: true }); | ||
| writeFileSync(outputPath, markdown); | ||
| writeFileSync(resolvedOutputPath, markdown); | ||
|
Comment on lines
+187
to
+190
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- imports and path helpers ---'
sed -n '1,80p' scripts/export-growth-metrics.mjs
printf '%s\n' '--- target code ---'
sed -n '160,205p' scripts/export-growth-metrics.mjs
printf '%s\n' '--- path helper references ---'
rg -n -C 4 'resolveOutputPath|outputPath|writeFileSync|mkdirSync' scripts/export-growth-metrics.mjs
printf '%s\n' '--- native path behavior ---'
node - <<'JS'
const path = require('node:path');
const samples = [
path.win32.resolve('metrics.md'),
path.win32.resolve('reports', 'metrics.md'),
path.posix.resolve('reports', 'metrics.md'),
];
for (const resolved of samples) {
const splitDir = resolved.split('/').slice(0, -1).join('/');
console.log(JSON.stringify({
resolved,
splitDir,
dirname: path.dirname(resolved),
win32Dirname: path.win32.dirname(resolved),
}));
}
JSRepository: nish3451/tinystudio-in Length of output: 7104 🏁 Script executed: #!/bin/bash
set -eu
file=$(fd -t f -i 'operator-cli\.mjs$' . | head -n 1)
if [ -z "$file" ]; then
printf '%s\n' 'operator-cli.mjs not found'
exit 0
fi
printf '--- %s ---\n' "$file"
rg -n -C 8 'resolveOutputPath|from "node:path"|path\.resolve|resolve\(' "$file"
printf '%s\n' '--- related path tests and callers ---'
rg -n -C 3 'resolveOutputPath' . -g '!node_modules' -g '!dist' -g '!build'Repository: nish3451/tinystudio-in Length of output: 251 🏁 Script executed: #!/bin/bash
set -eu
file=$(fd -t f -i 'operator-cli\.mjs$' . | head -n 1)
if [ -z "$file" ]; then
printf '%s\n' 'operator-cli.mjs not found'
exit 0
fi
printf -- '--- %s ---\n' "$file"
rg -n -C 8 'resolveOutputPath|from "node:path"|path\.resolve|resolve\(' "$file"
printf '%s\n' '--- related path tests and callers ---'
rg -n -C 3 'resolveOutputPath' . -g '!node_modules' -g '!dist' -g '!build'Repository: nish3451/tinystudio-in Length of output: 30818 Use On Windows, Import 🤖 Prompt for AI Agents |
||
|
|
||
| const result = { | ||
| status: "created", | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,11 +1,13 @@ | ||||||||||||||||||||||
| #!/usr/bin/env node | ||||||||||||||||||||||
| import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; | ||||||||||||||||||||||
| import { join, resolve } from "node:path"; | ||||||||||||||||||||||
| import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; | ||||||||||||||||||||||
| import { join } from "node:path"; | ||||||||||||||||||||||
| import { localIsoDate } from "./date-utils.mjs"; | ||||||||||||||||||||||
| import { sendChannelGuidance } from "./lib/send-channel-guidance.mjs"; | ||||||||||||||||||||||
| import { listOutboundProspectFolders } from "./lib/outbound-prospects.mjs"; | ||||||||||||||||||||||
| import { codeRoot, runRepoJson as runJson, serviceRoot } from "./lib/runtime-roots.mjs"; | ||||||||||||||||||||||
| import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs"; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| handleHelp(process.argv.slice(2), `Usage: npm run growth:dashboard -- [--plain] [--output=runs/internal-dashboard.md] [--html=runs/internal-dashboard.html]`); | ||||||||||||||||||||||
| const outputArg = process.argv.find((arg) => arg.startsWith("--output=")); | ||||||||||||||||||||||
| const htmlArg = process.argv.find((arg) => arg.startsWith("--html=")); | ||||||||||||||||||||||
| const plain = process.argv.includes("--plain"); | ||||||||||||||||||||||
|
|
@@ -54,7 +56,9 @@ catch (error) { | |||||||||||||||||||||
| const output = String(error.stderr || error.message || ""); | ||||||||||||||||||||||
| serviceQueue = { items: [], counts: { blocked: 1 }, error: output.match(/service:queue failed:[^\n]*/)?.[0] || "service queue validation failed" }; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| const parity = runJson(["scripts/check-market-parity-readiness.mjs", "--skip-kit", "--output=/tmp/tinystudio-internal-dashboard-parity.md"]); | ||||||||||||||||||||||
| const parityScratchPath = "runs/.internal-dashboard-parity.md"; | ||||||||||||||||||||||
| const parity = runJson(["scripts/check-market-parity-readiness.mjs", "--skip-kit", `--output=${parityScratchPath}`]); | ||||||||||||||||||||||
| try { rmSync(join(serviceRoot, parityScratchPath), { force: true }); } catch {} | ||||||||||||||||||||||
|
Comment on lines
+59
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Remove the parity scratch file when the parity command fails.
Proposed fix const parityScratchPath = "runs/.internal-dashboard-parity.md";
-const parity = runJson(["scripts/check-market-parity-readiness.mjs", "--skip-kit", `--output=${parityScratchPath}`]);
-try { rmSync(join(serviceRoot, parityScratchPath), { force: true }); } catch {}
+let parity;
+try {
+ parity = runJson(["scripts/check-market-parity-readiness.mjs", "--skip-kit", `--output=${parityScratchPath}`]);
+} finally {
+ rmSync(join(serviceRoot, parityScratchPath), { force: true });
+}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Comment on lines
+59
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||||||||||||||||||||||
| const todayView = runJson(["scripts/show-growth-command-center.mjs", "--limit=12"]); | ||||||||||||||||||||||
| const marketProof = existsSync(join(serviceRoot, "prospects/loom-links.txt")) | ||||||||||||||||||||||
| ? runJson(["scripts/export-market-proof-cockpit.mjs"]) | ||||||||||||||||||||||
|
|
@@ -482,8 +486,8 @@ const html = `<!doctype html> | |||||||||||||||||||||
| </html> | ||||||||||||||||||||||
| `; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| write(resolve(serviceRoot, outputPath), markdown); | ||||||||||||||||||||||
| write(resolve(serviceRoot, htmlPath), html); | ||||||||||||||||||||||
| write(resolveOutputPath(outputPath), markdown); | ||||||||||||||||||||||
| write(resolveOutputPath(htmlPath, { flag: "--html" }), html); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const result = { | ||||||||||||||||||||||
| status: doctor.status === "blocked" || parity.status === "not-11-10-yet" || serviceQueueBlocked > 0 || clientIntegrityBlocked > 0 ? "attention-needed" : "ready", | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,9 @@ | |
| import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; | ||
| import { join } from "node:path"; | ||
| import { listOutboundProspectFolders } from "./lib/outbound-prospects.mjs"; | ||
| import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs"; | ||
|
|
||
| handleHelp(process.argv.slice(2), `Usage: npm run prospect:score-cockpit -- [--limit=10] [--output=prospects/lead-scoring-cockpit.html]`); | ||
| const limitArg = process.argv.find((arg) => arg.startsWith("--limit=")); | ||
| const limit = limitArg ? Number(limitArg.split("=")[1]) : 10; | ||
| const outputArg = process.argv.find((arg) => arg.startsWith("--output=")); | ||
|
|
@@ -295,9 +297,10 @@ const html = `<!doctype html> | |
| </html> | ||
| `; | ||
|
|
||
| const outputDir = outputPath.split("/").slice(0, -1).join("/"); | ||
| const resolvedOutputPath = resolveOutputPath(outputPath); | ||
| const outputDir = resolvedOutputPath.split("/").slice(0, -1).join("/"); | ||
| if (outputDir) mkdirSync(outputDir, { recursive: true }); | ||
| writeFileSync(outputPath, html); | ||
| writeFileSync(resolvedOutputPath, html); | ||
|
Comment on lines
+300
to
+303
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Use On Windows, Use 🤖 Prompt for AI Agents |
||
|
|
||
| console.log(JSON.stringify({ | ||
| status: "created", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,8 +3,10 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; | |
| import { localIsoDate } from "./date-utils.mjs"; | ||
| import { NO_GUARANTEE_CLIENT_SENTENCE } from "./lib/service-contract.mjs"; | ||
| import { runRepoJson as runJson } from "./lib/runtime-roots.mjs"; | ||
| import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs"; | ||
|
|
||
| const args = process.argv.slice(2); | ||
| handleHelp(args, `Usage: npm run market:benchmark -- [--output=docs/strategy/market-parity-benchmark-2026.md] [--ops=growth-brain/ops/competitive-proof-matrix.md] [--html=growth-brain/ops/competitive-proof-matrix.html]`); | ||
| const outputArg = args.find((arg) => arg.startsWith("--output=")); | ||
| const opsArg = args.find((arg) => arg.startsWith("--ops=")); | ||
| const htmlArg = args.find((arg) => arg.startsWith("--html=")); | ||
|
|
@@ -343,9 +345,9 @@ const html = `<!doctype html> | |
| </html> | ||
| `; | ||
|
|
||
| write(outputPath, markdown); | ||
| write(opsPath, markdown); | ||
| write(htmlPath, html); | ||
| write(resolveOutputPath(outputPath), markdown); | ||
| write(resolveOutputPath(opsPath, { flag: "--ops" }), markdown); | ||
|
Comment on lines
+348
to
+349
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a secondary path is unsafe—for example, Useful? React with 👍 / 👎. |
||
| write(resolveOutputPath(htmlPath, { flag: "--html" }), html); | ||
|
|
||
| const status = proofBars.some((item) => item.status === "fail") | ||
| ? "market-proof-needed" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,9 @@ import { sendChannelGuidance } from "./lib/send-channel-guidance.mjs"; | |
| import { routedContactPlan } from "./lib/contact-route.mjs"; | ||
| import { canonicalProspectAsk } from "./lib/canonical-service-copy.mjs"; | ||
| import { listOutboundProspectFolders } from "./lib/outbound-prospects.mjs"; | ||
| import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs"; | ||
|
|
||
| handleHelp(process.argv.slice(2), `Usage: npm run market:proof-run -- [--limit=5] [--skip-kit] [--output=growth-brain/ops/11-10-proof-run.md] [--loom-links=prospects/loom-links.txt]`); | ||
| const outputArg = process.argv.find((arg) => arg.startsWith("--output=")); | ||
| const outputPath = outputArg ? outputArg.split("=")[1] : "growth-brain/ops/11-10-proof-run.md"; | ||
| const loomLinksArg = process.argv.find((arg) => arg.startsWith("--loom-links=")); | ||
|
|
@@ -22,6 +24,7 @@ const loomLinksPath = loomLinksArg | |
| : outputPath.startsWith("prospects/kit-") | ||
| ? "prospects/kit-proof-run-loom-links.txt" | ||
| : "prospects/loom-links.txt"; | ||
| const resolvedLoomLinksPath = resolveOutputPath(loomLinksPath, { flag: "--loom-links" }); | ||
|
|
||
| function runJson(args) { | ||
| const output = execFileSync("node", args, { | ||
|
|
@@ -226,7 +229,7 @@ const recordingBatch = prospects | |
| .slice(0, limit); | ||
|
|
||
| const directionGate = directionGateCounts( | ||
| read(loomLinksPath).split("\n").map(parseLoomSheetLine).filter(Boolean), | ||
| read(resolvedLoomLinksPath).split("\n").map(parseLoomSheetLine).filter(Boolean), | ||
| prospects | ||
| ); | ||
|
|
||
|
|
@@ -335,17 +338,18 @@ Source: the direction dossier gates this proof run on 5 approved Looms (recorded | |
| - Missing: ${40 - directionGate.qualifiedTouches} qualified touch(es) are still absent; ${directionGate.qualifiedProspectsWithTouches} qualified prospect(s) currently carry touch evidence. | ||
| `; | ||
|
|
||
| const outputDir = outputPath.split("/").slice(0, -1).join("/"); | ||
| const resolvedOutputPath = resolveOutputPath(outputPath); | ||
| const outputDir = resolvedOutputPath.split("/").slice(0, -1).join("/"); | ||
| if (outputDir) mkdirSync(outputDir, { recursive: true }); | ||
| writeFileSync(outputPath, markdown); | ||
| writeFileSync(resolvedOutputPath, markdown); | ||
|
|
||
| const loomLinksDir = loomLinksPath.split("/").slice(0, -1).join("/"); | ||
| const loomLinksDir = resolvedLoomLinksPath.split("/").slice(0, -1).join("/"); | ||
| if (loomLinksDir) mkdirSync(loomLinksDir, { recursive: true }); | ||
| const existingLoomRows = read(loomLinksPath).split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")); | ||
| const existingLoomRows = read(resolvedLoomLinksPath).split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")); | ||
| const existingLoomPaths = new Set(existingLoomRows.map((line) => line.split("|")[0].trim())); | ||
| const generatedLoomRows = recordingBatch.length ? loomSheetRows.split("\n").filter((line) => !existingLoomPaths.has(line.split("|")[0].trim())) : existingLoomRows.length ? [] : [loomSheetRows]; | ||
| const mergedLoomRows = [...existingLoomRows, ...generatedLoomRows]; | ||
| writeFileSync(loomLinksPath, `# Replace LOOM_URL with each real Loom share link, or run: npm run market:after-recording -- --from-clipboard\n# Fast format after recording: paste either URL-only lines in this exact order, or prospects/prospect-slug|https://www.loom.com/share/...\n# Full format still works: prospects/prospect-slug|https://www.loom.com/share/...|approved|fault note|impact note|fix note|ask note\n\n${mergedLoomRows.join("\n")}\n`); | ||
| writeFileSync(resolvedLoomLinksPath, `# Replace LOOM_URL with each real Loom share link, or run: npm run market:after-recording -- --from-clipboard\n# Fast format after recording: paste either URL-only lines in this exact order, or prospects/prospect-slug|https://www.loom.com/share/...\n# Full format still works: prospects/prospect-slug|https://www.loom.com/share/...|approved|fault note|impact note|fix note|ask note\n\n${mergedLoomRows.join("\n")}\n`); | ||
|
Comment on lines
+341
to
+352
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -e
file=$(fd -t f 'export-market-proof-run\.mjs$' . | head -n 1)
printf '%s\n' "FILE=$file"
sed -n '1,80p' "$file"
sed -n '325,365p' "$file"
printf '\nPATH HELPERS AND CALL SITES:\n'
rg -n -C 3 'resolveOutputPath|resolvedOutputPath|resolvedLoomLinksPath|loomLinksDir|outputDir|mkdirSync|writeFileSync' "$file"
printf '\nPACKAGE CONTEXT:\n'
fd -t f 'package\.json$|package-lock\.json$|pnpm-lock\.yaml$|yarn\.lock$' . | head -n 20Repository: nish3451/tinystudio-in Length of output: 9852 🏁 Script executed: node - <<'JS'
const path = require('node:path');
const cases = [
String.raw`C:\repo\out\proof.md`,
String.raw`C:\repo\out\loom-links.md`,
String.raw`\\server\share\repo\out\proof.md`,
`/repo/out/proof.md`,
];
for (const value of cases) {
const splitDir = value.split('/').slice(0, -1).join('/');
console.log(JSON.stringify({
value,
splitDir,
dirnameOnLinux: path.dirname(value),
win32Dirname: path.win32.dirname(value),
}));
}
JSRepository: nish3451/tinystudio-in Length of output: 611 🏁 Script executed: #!/bin/bash
set -e
helper=$(fd -t f 'operator-cli\.mjs$' . | head -n 1)
printf '%s\n' "HELPER=$helper"
ast-grep outline "$helper"
rg -n -C 8 'function resolveOutputPath|const resolveOutputPath|resolveOutputPath|path\.|win32|platform' "$helper"Repository: nish3451/tinystudio-in Length of output: 1340 🏁 Script executed: node - <<'JS'
const path = require('node:path');
const samples = [
String.raw`C:\repo\out\proof.md`,
String.raw`C:/repo/out/proof.md`,
String.raw`\\server\share\repo\out\proof.md`,
'growth-brain/ops/11-10-proof-run.md',
'/tmp/repo/out/proof.md',
];
for (const value of samples) {
const splitDir = value.split('/').slice(0, -1).join('/');
console.log(JSON.stringify({
value,
splitDir,
splitDirTruthy: Boolean(splitDir),
posixDirname: path.dirname(value),
win32Dirname: path.win32.dirname(value),
}));
}
JSRepository: nish3451/tinystudio-in Length of output: 888 🏁 Script executed: #!/bin/bash
set -e
helper=$(fd -t f 'operator-cli\.mjs$' . | head -n 1)
sed -n '1,55p' "$helper"Repository: nish3451/tinystudio-in Length of output: 2535 🏁 Script executed: node - <<'JS'
const path = require('node:path');
const root = String.raw`C:\repo`;
const inputs = [
'growth-brain/ops/11-10-proof-run.md',
'prospects/loom-links.txt',
String.raw`growth-brain\ops\11-10-proof-run.md`,
String.raw`C:\repo\growth-brain\ops\11-10-proof-run.md`,
];
for (const input of inputs) {
const resolved = path.win32.resolve(root, input);
const splitDir = resolved.split('/').slice(0, -1).join('/');
console.log(JSON.stringify({
input,
resolved,
splitDir,
mkdirWouldRun: Boolean(splitDir),
correctDir: path.win32.dirname(resolved),
}));
}
JSRepository: nish3451/tinystudio-in Length of output: 895 Use On Windows, 🤖 Prompt for AI Agents |
||
|
|
||
| console.log(JSON.stringify({ | ||
| status: "created", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,9 @@ | |
| import { mkdirSync, writeFileSync } from "node:fs"; | ||
| import { localIsoDate } from "./date-utils.mjs"; | ||
| import { agencyConfig } from "./lib/agency-config.mjs"; | ||
| import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs"; | ||
|
|
||
| handleHelp(process.argv.slice(2), `Usage: npm run growth:proof -- [--output=growth-brain/ops/proof-library.md]`); | ||
| const outputArg = process.argv.find((arg) => arg.startsWith("--output=")); | ||
| const outputPath = outputArg ? outputArg.split("=")[1] : "growth-brain/ops/proof-library.md"; | ||
| const today = localIsoDate(); | ||
|
|
@@ -63,9 +65,10 @@ This tracked file contains operating rules and approved product facts only. It n | |
| 3. Add only the final human-approved artifact and its permission record. | ||
| `; | ||
|
|
||
| const outputDir = outputPath.split("/").slice(0, -1).join("/"); | ||
| const resolvedOutputPath = resolveOutputPath(outputPath); | ||
| const outputDir = resolvedOutputPath.split("/").slice(0, -1).join("/"); | ||
| if (outputDir) mkdirSync(outputDir, { recursive: true }); | ||
| writeFileSync(outputPath, content); | ||
| writeFileSync(resolvedOutputPath, content); | ||
|
Comment on lines
+68
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- file context ---'
sed -n '1,110p' scripts/export-proof-library.mjs
printf '%s\n' '--- path helper references ---'
rg -n -C 4 'resolveOutputPath|split\("/"\)|dirname\(' scripts
printf '%s\n' '--- runtime path behavior ---'
node - <<'JS'
const { dirname, win32, posix } = require('node:path');
const samples = [
'/tmp/out/proof.json',
'C:\\work\\out\\proof.json',
'\\\\server\\share\\out\\proof.json',
];
for (const value of samples) {
console.log(JSON.stringify({
value,
splitSlashParent: value.split('/').slice(0, -1).join('/'),
dirname: dirname(value),
win32Dirname: win32.dirname(value),
posixDirname: posix.dirname(value),
}));
}
JSRepository: nish3451/tinystudio-in Length of output: 50379 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- operator-cli path helper ---'
rg -n -C 12 'function resolveOutputPath|const resolveOutputPath|export .*resolveOutputPath' scripts/lib/operator-cli.mjs
printf '%s\n' '--- relevant imports and call sites ---'
sed -n '1,95p' scripts/lib/operator-cli.mjs
sed -n '1,10p' scripts/export-proof-library.mjs
sed -n '64,75p' scripts/export-proof-library.mjs
printf '%s\n' '--- isolated platform behavior ---'
node - <<'JS'
const { dirname, win32 } = require('node:path');
for (const value of [
'C:\\work\\out\\proof.json',
'\\\\server\\share\\out\\proof.json',
'C:/work/out/proof.json',
]) {
console.log(JSON.stringify({
value,
slashSplit: value.split('/').slice(0, -1).join('/'),
hostDirname: dirname(value),
windowsDirname: win32.dirname(value),
}));
}
JSRepository: nish3451/tinystudio-in Length of output: 5188 Use On Windows, 🤖 Prompt for AI Agents |
||
|
|
||
| console.log(JSON.stringify({ | ||
| status: "created", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,9 @@ import { sendChannelGuidance } from "./lib/send-channel-guidance.mjs"; | |
| import { isValidLoomUrl } from "./lib/loom-url.mjs"; | ||
| import { routedContactPlan, routeToChannel } from "./lib/contact-route.mjs"; | ||
| import { listOutboundProspectFolders } from "./lib/outbound-prospects.mjs"; | ||
| import { handleHelp, resolveOutputPath } from "./lib/operator-cli.mjs"; | ||
|
|
||
| handleHelp(process.argv.slice(2), `Usage: npm run prospect:outbox -- [--limit=20] [--output=prospects/outbox.html]`); | ||
| const limitArg = process.argv.find((arg) => arg.startsWith("--limit=")); | ||
| const limit = limitArg ? Number(limitArg.split("=")[1]) : 20; | ||
| const outputArg = process.argv.find((arg) => arg.startsWith("--output=")); | ||
|
|
@@ -642,9 +644,10 @@ const html = `<!doctype html> | |
| </html> | ||
| `; | ||
|
|
||
| const outputDir = outputPath.split("/").slice(0, -1).join("/"); | ||
| const resolvedOutputPath = resolveOutputPath(outputPath); | ||
| const outputDir = resolvedOutputPath.split("/").slice(0, -1).join("/"); | ||
| if (outputDir) mkdirSync(outputDir, { recursive: true }); | ||
| writeFileSync(outputPath, html); | ||
| writeFileSync(resolvedOutputPath, html); | ||
|
Comment on lines
+647
to
+650
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- imports and target code ---'
sed -n '1,45p' scripts/export-prospect-outbox.mjs
sed -n '620,665p' scripts/export-prospect-outbox.mjs
printf '%s\n' '--- path-related usage ---'
rg -n -C 3 'resolveOutputPath|outputDir|writeFileSync|mkdirSync|dirname|outputPath' scripts/export-prospect-outbox.mjs
printf '%s\n' '--- repository platform assumptions ---'
rg -n -C 2 'export-prospect-outbox|Windows|win32|path separator|dirname' README.md package.json scripts test tests 2>/dev/null || trueRepository: nish3451/tinystudio-in Length of output: 50378 🏁 Script executed: #!/bin/bash
set -eu
sed -n '1,45p' scripts/export-prospect-outbox.mjs
sed -n '620,665p' scripts/export-prospect-outbox.mjs
rg -n -C 3 'resolveOutputPath|outputDir|writeFileSync|mkdirSync|dirname|outputPath' scripts/export-prospect-outbox.mjsRepository: nish3451/tinystudio-in Length of output: 4604 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- resolveOutputPath implementation ---'
sed -n '1,120p' scripts/lib/operator-cli.mjs
printf '%s\n' '--- all resolveOutputPath call sites ---'
rg -n -C 4 'resolveOutputPath' scripts --glob '*.mjs'
printf '%s\n' '--- cross-platform path behavior probe ---'
node - <<'JS'
const path = require("node:path");
for (const p of [
"C:\\repo\\prospects\\outbox.html",
"C:\\repo\\outbox.html",
"prospects/outbox.html",
"/repo/prospects/outbox.html",
"outbox.html"
]) {
console.log(JSON.stringify({
input: p,
posixSplitDir: p.split("/").slice(0, -1).join("/"),
win32Dirname: path.win32.dirname(p),
posixDirname: path.posix.dirname(p)
}));
}
JSRepository: nish3451/tinystudio-in Length of output: 38115 🏁 Script executed: #!/bin/bash
set -eu
sed -n '1,120p' scripts/lib/operator-cli.mjs
rg -n -C 4 'resolveOutputPath' scripts --glob '*.mjs'
node - <<'JS'
const path = require("node:path");
for (const p of [
"C:\\repo\\prospects\\outbox.html",
"C:\\repo\\outbox.html",
"prospects/outbox.html",
"/repo/prospects/outbox.html",
"outbox.html"
]) {
console.log(JSON.stringify({
input: p,
posixSplitDir: p.split("/").slice(0, -1).join("/"),
win32Dirname: path.win32.dirname(p),
posixDirname: path.posix.dirname(p)
}));
}
JSRepository: nish3451/tinystudio-in Length of output: 37990 Use On Windows, 🤖 Prompt for AI Agents |
||
|
|
||
| console.log(JSON.stringify({ | ||
| status: "created", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: nish3451/tinystudio-in
Length of output: 5843
🏁 Script executed:
Repository: nish3451/tinystudio-in
Length of output: 2545
🏁 Script executed:
Repository: nish3451/tinystudio-in
Length of output: 4901
🏁 Script executed:
Repository: nish3451/tinystudio-in
Length of output: 370
Use
dirname()for the resolved output path.On Windows,
split("/")produces an emptyoutputDir, somkdirSync()does not create missing parent directories.writeFileSync()then fails withENOENT. Importdirnamefromnode:pathand usedirname(resolvedOutputPath).🤖 Prompt for AI Agents