tenjin-cli 0.1.0-alpha.2 → 0.1.0-alpha.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 +11 -8
- package/dist/{buy-U35DQ6XM.js → buy-EE6SEXP6.js} +7 -7
- package/dist/{candidate-RLYCQMF5.js → candidate-ERMSYYQZ.js} +3 -3
- package/dist/{chunk-QNP7H47L.js → chunk-4ONXEEQL.js} +2 -2
- package/dist/{chunk-YIYQ2ZNT.js → chunk-662MSGZB.js} +2 -2
- package/dist/{chunk-7PHSZKQS.js → chunk-AWPAWHNX.js} +3 -3
- package/dist/{chunk-27ONARHV.js → chunk-DXTQ3CSI.js} +3 -3
- package/dist/{chunk-IYLHQMT5.js → chunk-ESPNTPQ2.js} +4 -8
- package/dist/chunk-ESPNTPQ2.js.map +1 -0
- package/dist/{chunk-N62ROTMC.js → chunk-FJPLRISB.js} +2 -2
- package/dist/{chunk-N62ROTMC.js.map → chunk-FJPLRISB.js.map} +1 -1
- package/dist/{chunk-UPIEAMGH.js → chunk-GO75V3NZ.js} +2 -2
- package/dist/{chunk-TFGSOPWR.js → chunk-XHC6FCGZ.js} +3 -3
- package/dist/{cli-4NNBEQYH.js → cli-SBXI5AMS.js} +18 -18
- package/dist/{config-2VSXWWHK.js → config-VELDKLHY.js} +4 -4
- package/dist/{doctor-JJPNIBYH.js → doctor-T3KWNNP2.js} +5 -5
- package/dist/index.js +1 -1
- package/dist/{inspect-JGYJWFDP.js → inspect-LMKOXE4Y.js} +7 -7
- package/dist/{install-ZQRMSRGW.js → install-LEX3ENQ6.js} +21 -12
- package/dist/install-LEX3ENQ6.js.map +1 -0
- package/dist/{lookup-WYVTOMVY.js → lookup-ZPN2Q67N.js} +22 -6
- package/dist/lookup-ZPN2Q67N.js.map +1 -0
- package/dist/{outcome-HKKT6QIA.js → outcome-N7TAK6WR.js} +6 -6
- package/dist/{publish-D76B3MBK.js → publish-BKBQQAW4.js} +12 -14
- package/dist/publish-BKBQQAW4.js.map +1 -0
- package/dist/{wallet-6QKS6UHW.js → wallet-CWEMYVIH.js} +2 -2
- package/package.json +1 -1
- package/skills/tenjin-publish/SKILL.md +40 -25
- package/skills/tenjin-search/SKILL.md +29 -18
- package/dist/chunk-IYLHQMT5.js.map +0 -1
- package/dist/install-ZQRMSRGW.js.map +0 -1
- package/dist/lookup-WYVTOMVY.js.map +0 -1
- package/dist/publish-D76B3MBK.js.map +0 -1
- /package/dist/{buy-U35DQ6XM.js.map → buy-EE6SEXP6.js.map} +0 -0
- /package/dist/{candidate-RLYCQMF5.js.map → candidate-ERMSYYQZ.js.map} +0 -0
- /package/dist/{chunk-QNP7H47L.js.map → chunk-4ONXEEQL.js.map} +0 -0
- /package/dist/{chunk-YIYQ2ZNT.js.map → chunk-662MSGZB.js.map} +0 -0
- /package/dist/{chunk-7PHSZKQS.js.map → chunk-AWPAWHNX.js.map} +0 -0
- /package/dist/{chunk-27ONARHV.js.map → chunk-DXTQ3CSI.js.map} +0 -0
- /package/dist/{chunk-UPIEAMGH.js.map → chunk-GO75V3NZ.js.map} +0 -0
- /package/dist/{chunk-TFGSOPWR.js.map → chunk-XHC6FCGZ.js.map} +0 -0
- /package/dist/{cli-4NNBEQYH.js.map → cli-SBXI5AMS.js.map} +0 -0
- /package/dist/{config-2VSXWWHK.js.map → config-VELDKLHY.js.map} +0 -0
- /package/dist/{doctor-JJPNIBYH.js.map → doctor-T3KWNNP2.js.map} +0 -0
- /package/dist/{inspect-JGYJWFDP.js.map → inspect-LMKOXE4Y.js.map} +0 -0
- /package/dist/{outcome-HKKT6QIA.js.map → outcome-N7TAK6WR.js.map} +0 -0
- /package/dist/{wallet-6QKS6UHW.js.map → wallet-CWEMYVIH.js.map} +0 -0
|
@@ -2,18 +2,19 @@ import { createRequire as __tenjinCreateRequire } from 'node:module'; const requ
|
|
|
2
2
|
import {
|
|
3
3
|
collectDoctorChecks,
|
|
4
4
|
renderDoctorHuman
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-AWPAWHNX.js";
|
|
6
|
+
import "./chunk-4ONXEEQL.js";
|
|
7
|
+
import "./chunk-FJPLRISB.js";
|
|
8
8
|
import {
|
|
9
9
|
persistPublishMode
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-XHC6FCGZ.js";
|
|
11
|
+
import "./chunk-GO75V3NZ.js";
|
|
12
12
|
import {
|
|
13
|
+
CONFIG_DEFAULTS,
|
|
13
14
|
PublishModeSchema,
|
|
14
15
|
loadRawConfig,
|
|
15
16
|
parsePublishModeFlag
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-ESPNTPQ2.js";
|
|
17
18
|
import "./chunk-YN5SHIXQ.js";
|
|
18
19
|
import "./chunk-2I4QTOLB.js";
|
|
19
20
|
import "./chunk-YBMMV4Y4.js";
|
|
@@ -101,7 +102,14 @@ async function runInstall(input, ctx, deps = {}) {
|
|
|
101
102
|
humanLines.push(publishModeLine(ctx.io, publishMode, dryRun));
|
|
102
103
|
return { data, humanLines };
|
|
103
104
|
}
|
|
104
|
-
var
|
|
105
|
+
var DEFAULT_MODE = CONFIG_DEFAULTS.publish.mode;
|
|
106
|
+
var PUBLISH_MODE_PROMPT = [
|
|
107
|
+
"Publish consent mode?",
|
|
108
|
+
" review - asks a yes/no for every publish",
|
|
109
|
+
" auto - publishes automatically when the secret-scan is clean (including answers your agent derives)",
|
|
110
|
+
" full-auto - publishes even past warnings; only detected secrets stop it",
|
|
111
|
+
`[${DEFAULT_MODE}]: `
|
|
112
|
+
].join("\n");
|
|
105
113
|
async function selectPublishMode(flag, ctx, deps, dryRun) {
|
|
106
114
|
if (flag !== void 0) {
|
|
107
115
|
if (!dryRun) await persistPublishMode(ctx.dataDir, flag);
|
|
@@ -112,11 +120,11 @@ async function selectPublishMode(flag, ctx, deps, dryRun) {
|
|
|
112
120
|
return { value: config.publish.mode, source: "existing" };
|
|
113
121
|
}
|
|
114
122
|
const interactive = ctx.flags.json !== true && (deps.isInteractive ?? (ctx.io.isTTY && Boolean(process.stdin.isTTY)));
|
|
115
|
-
if (dryRun || !interactive) return { value:
|
|
123
|
+
if (dryRun || !interactive) return { value: DEFAULT_MODE, source: "default-skipped" };
|
|
116
124
|
const prompt = deps.promptMode ?? defaultPromptMode;
|
|
117
125
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
118
126
|
const answer = (await prompt(PUBLISH_MODE_PROMPT)).trim();
|
|
119
|
-
if (answer.length === 0) return { value:
|
|
127
|
+
if (answer.length === 0) return { value: DEFAULT_MODE, source: "default-skipped" };
|
|
120
128
|
const parsed = PublishModeSchema.safeParse(answer);
|
|
121
129
|
if (parsed.success) {
|
|
122
130
|
await persistPublishMode(ctx.dataDir, parsed.data);
|
|
@@ -124,9 +132,10 @@ async function selectPublishMode(flag, ctx, deps, dryRun) {
|
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
ctx.io.stderr.write(
|
|
127
|
-
|
|
135
|
+
`Unrecognized mode; keeping the default (${DEFAULT_MODE}). Change it with \`tenjin config set publish.mode\`.
|
|
136
|
+
`
|
|
128
137
|
);
|
|
129
|
-
return { value:
|
|
138
|
+
return { value: DEFAULT_MODE, source: "default-skipped" };
|
|
130
139
|
}
|
|
131
140
|
function parseModeFlag(value) {
|
|
132
141
|
return parsePublishModeFlag(value, "--publish-mode");
|
|
@@ -375,4 +384,4 @@ async function assertSkillsSource(dir) {
|
|
|
375
384
|
export {
|
|
376
385
|
runInstall
|
|
377
386
|
};
|
|
378
|
-
//# sourceMappingURL=install-
|
|
387
|
+
//# sourceMappingURL=install-LEX3ENQ6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/install.ts","../src/lib/skills-source.ts"],"sourcesContent":["import { existsSync, statSync } from 'node:fs';\nimport { readFile, readdir, rm } from 'node:fs/promises';\nimport { createInterface } from 'node:readline';\nimport { fileURLToPath } from 'node:url';\nimport { delimiter, join, relative } from 'node:path';\nimport { homedir } from 'node:os';\nimport { z } from 'zod';\nimport { styleText } from 'node:util';\nimport { Stream } from 'node:stream';\nimport { CliError } from '../lib/errors';\nimport { writeFileAtomic } from '../lib/atomic-json';\nimport { resolveSkillsSource, SKILL_NAMES } from '../lib/skills-source';\nimport {\n CONFIG_DEFAULTS,\n loadRawConfig,\n PublishModeSchema,\n parsePublishModeFlag,\n} from '../lib/config';\nimport type { PublishMode } from '../lib/config';\nimport { persistPublishMode } from './config';\nimport { collectDoctorChecks, renderDoctorHuman } from './doctor';\nimport type { DoctorDeps, DoctorChecks } from './doctor';\nimport type { Io } from '../lib/output';\nimport type { CommandContext, CommandResult } from '../context';\n\nconst HARNESSES = ['claude', 'codex', 'shared'] as const;\ntype Harness = (typeof HARNESSES)[number];\n\nconst InstallInputSchema = z.object({\n harness: z.array(z.string()).optional(),\n dryRun: z.boolean().optional(),\n publishMode: z.string().optional(),\n});\nexport type InstallInput = z.infer<typeof InstallInputSchema>;\n\n/** A visible text-prompt seam (like passphrase's PromptFn); injected in tests. */\nexport type PromptFn = (label: string) => Promise<string>;\n\ntype PublishModeSource = 'flag' | 'existing' | 'prompt' | 'default-skipped';\ninterface PublishModeSelection {\n value: PublishMode;\n source: PublishModeSource;\n}\n\n/**\n * The one line `install` keeps in an AGENTS.md so Codex (which reads AGENTS.md as\n * global guidance, not the raw ~/.agents/skills scan) knows the skills are there.\n * The HTML-comment marker is how re-runs stay idempotent: its presence means the\n * line is already written, so we never append a second copy.\n */\nconst AGENTS_MARKER = '<!-- tenjin-cli:skills -->';\n\n/**\n * The Codex config.toml rule the user must add by hand. We PRINT it, never edit\n * config.toml: Codex's default workspace-write sandbox blocks network, which would\n * make every paid x402 call fail (or prompt) until this is set.\n */\nconst CODEX_NETWORK_RULE = '[sandbox_workspace_write]\\nnetwork_access = true';\n\n/** Per-skill install outcome for one harness target. */\ninterface SkillResult {\n name: string;\n status: 'installed' | 'updated' | 'up-to-date' | 'would-install' | 'would-update';\n}\n\ninterface AgentsMdResult {\n path: string;\n status: 'appended' | 'already-present' | 'would-append';\n}\n\ninterface HarnessResult {\n harness: Harness;\n detected: boolean;\n detectedBy: string[];\n skillsDir: string;\n skills: SkillResult[];\n agentsMd?: AgentsMdResult;\n codexNetworkRule?: string;\n notes: string[];\n warnings: string[];\n}\n\nexport interface InstallDeps {\n /** Home directory root for harness detection + skill destinations. Tests inject a temp dir. */\n homeDir?: string;\n /** The packaged skills source directory. Defaults to resolving it from this module's location. */\n skillsSourceDir?: string;\n /** PATH probe for `claude`/`codex` binaries. Injectable so tests never depend on the real PATH. */\n which?: (bin: string) => boolean;\n /** Environment (PATH for the default `which`). Defaults to process.env. */\n env?: NodeJS.ProcessEnv;\n /** Doctor check collector; defaults to the real one. Injected in tests to avoid the network. */\n collectChecks?: (ctx: CommandContext) => Promise<DoctorChecks>;\n /** Deps forwarded to the default doctor collector (e.g. a canned fetch). */\n doctorDeps?: DoctorDeps;\n /** Interactive text-prompt seam for the publish-mode question; defaults to a stdin reader. */\n promptMode?: PromptFn;\n /** Whether an interactive prompt is possible; defaults to the TTY facts. */\n isInteractive?: boolean;\n}\n\n/**\n * `tenjin install`: detect the installed harness(es), copy the packaged skills into\n * each one's skills directory, wire the AGENTS.md pointer + print the Codex sandbox\n * rule where relevant, THEN run the doctor checks as the final step (D39: doctor is\n * diagnostics run last, never a required first hop; a doctor problem is surfaced but\n * never fails the wiring that already succeeded). Idempotent: a re-run reports\n * up-to-date and never duplicates the AGENTS.md line. `--dry-run` writes nothing.\n */\nexport async function runInstall(\n input: InstallInput,\n ctx: CommandContext,\n deps: InstallDeps = {},\n): Promise<CommandResult> {\n const parsed = InstallInputSchema.safeParse(input);\n if (!parsed.success) {\n throw new CliError('USAGE', 'Invalid install options', {\n fix: 'Run `tenjin install --help`.',\n details: parsed.error.issues,\n });\n }\n const dryRun = parsed.data.dryRun === true;\n // Validate --publish-mode UP FRONT so a bad value fails before any wiring, even\n // though the mode is only settled (and persisted) after the doctor phase below.\n const publishModeFlag =\n parsed.data.publishMode !== undefined ? parseModeFlag(parsed.data.publishMode) : undefined;\n const env = deps.env ?? process.env;\n const home = deps.homeDir ?? homedir();\n const which = deps.which ?? ((bin: string) => onPath(bin, env));\n\n const skillsSource =\n deps.skillsSourceDir ?? resolveSkillsSource(fileURLToPath(new URL('.', import.meta.url)));\n await assertSkillsSource(skillsSource);\n\n const plans = resolvePlans(parsed.data.harness, home, which);\n\n const harnesses: HarnessResult[] = [];\n for (const plan of plans) {\n harnesses.push(await applyPlan(plan, skillsSource, dryRun));\n }\n\n const collect = deps.collectChecks ?? ((c) => collectDoctorChecks(c, deps.doctorDeps ?? {}));\n const doctor = await collect(ctx);\n const doctorStatus = doctor.failure !== undefined ? 'fail' : 'pass';\n\n // After wiring + doctor, settle the publish consent mode: a flag sets it, an\n // already-configured mode is left alone, and an interactive setup asks once\n // (plain enter keeps the default WITHOUT writing, so `config get` stays truthful).\n const publishMode = await selectPublishMode(publishModeFlag, ctx, deps, dryRun);\n\n const data = {\n dryRun,\n skillsSource,\n harnesses,\n doctor: { status: doctorStatus, checks: doctor.checks },\n publishMode,\n };\n const humanLines = renderHuman(ctx.io, harnesses, doctor, dryRun);\n humanLines.push(publishModeLine(ctx.io, publishMode, dryRun));\n return { data, humanLines };\n}\n\n// --- Publish-mode selection (D38 setup) ------------------------------------------\n\n/** The default consent mode (also what a plain enter keeps, without writing). */\nconst DEFAULT_MODE: PublishMode = CONFIG_DEFAULTS.publish.mode;\n\nconst PUBLISH_MODE_PROMPT = [\n 'Publish consent mode?',\n ' review - asks a yes/no for every publish',\n ' auto - publishes automatically when the secret-scan is clean (including answers your agent derives)',\n ' full-auto - publishes even past warnings; only detected secrets stop it',\n `[${DEFAULT_MODE}]: `,\n].join('\\n');\n\n/**\n * Resolve (and, for an explicit choice, persist) the publish consent mode at\n * install time. Precedence: `--publish-mode` flag > an already-configured global\n * mode > an interactive prompt > the untouched default. Only an explicit choice\n * writes: a plain enter, a non-interactive run, `--dry-run`, or an unrecognized\n * answer all leave `publish.mode` unset so its provenance stays `default`.\n */\nasync function selectPublishMode(\n flag: PublishMode | undefined,\n ctx: CommandContext,\n deps: InstallDeps,\n dryRun: boolean,\n): Promise<PublishModeSelection> {\n if (flag !== undefined) {\n if (!dryRun) await persistPublishMode(ctx.dataDir, flag); // --dry-run: would-set only\n return { value: flag, source: 'flag' };\n }\n\n // Only the GLOBAL config file counts as \"already configured\" for setup: env/flag\n // are per-run and a project `.tenjin.json` is not this machine's global choice.\n const config = await loadRawConfig(ctx.dataDir);\n if (config.publish?.mode !== undefined) {\n return { value: config.publish.mode, source: 'existing' };\n }\n\n // --json implies non-interactive: a machine consumer must never sit behind a\n // prompt, even one on stderr. It overrides an injected isInteractive too, so the\n // only ways to set the mode under --json are --publish-mode or a prior config.\n const interactive =\n ctx.flags.json !== true &&\n (deps.isInteractive ?? (ctx.io.isTTY && Boolean(process.stdin.isTTY)));\n if (dryRun || !interactive) return { value: DEFAULT_MODE, source: 'default-skipped' };\n\n const prompt = deps.promptMode ?? defaultPromptMode;\n // Ask, allow ONE reprompt on an unrecognized answer, then fall back to the default.\n for (let attempt = 0; attempt < 2; attempt++) {\n const answer = (await prompt(PUBLISH_MODE_PROMPT)).trim();\n if (answer.length === 0) return { value: DEFAULT_MODE, source: 'default-skipped' }; // enter: no write\n const parsed = PublishModeSchema.safeParse(answer);\n if (parsed.success) {\n await persistPublishMode(ctx.dataDir, parsed.data);\n return { value: parsed.data, source: 'prompt' };\n }\n }\n ctx.io.stderr.write(\n `Unrecognized mode; keeping the default (${DEFAULT_MODE}). Change it with \\`tenjin config set publish.mode\\`.\\n`,\n );\n return { value: DEFAULT_MODE, source: 'default-skipped' };\n}\n\nfunction parseModeFlag(value: string): PublishMode {\n return parsePublishModeFlag(value, '--publish-mode');\n}\n\n/** A visible line-reader (prompt on stderr so stdout stays a single JSON object). */\nfunction defaultPromptMode(label: string): Promise<string> {\n return new Promise((resolve) => {\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n let settled = false;\n const settle = (value: string): void => {\n if (settled) return;\n settled = true;\n rl.close();\n resolve(value);\n };\n rl.once('close', () => settle('')); // ctrl-D mid-prompt reads as the default\n rl.question(label, (answer) => settle(answer));\n });\n}\n\nfunction publishModeLine(io: Io, selection: PublishModeSelection, dryRun: boolean): string {\n const detail: Record<PublishModeSource, string> = {\n flag: dryRun ? 'would set from --publish-mode' : 'set from --publish-mode',\n existing: 'already configured',\n prompt: 'saved',\n 'default-skipped': 'default; run interactively or pass --publish-mode to change',\n };\n return paint(io, 'bold', `publish mode: ${selection.value} (${detail[selection.source]})`);\n}\n\n// --- Detection + planning --------------------------------------------------------\n\ninterface HarnessPlan {\n harness: Harness;\n detected: boolean;\n detectedBy: string[];\n skillsDir: string;\n wiresAgentsMd: boolean;\n home: string;\n}\n\n/**\n * Turn detection (or an explicit --harness override) into the ordered, de-duped\n * list of targets to write. Codex and the shared fallback both land in\n * ~/.agents/skills (the harness-shared Agent Skills location), so a request for\n * both collapses to one target keyed by that directory.\n */\nfunction resolvePlans(\n override: string[] | undefined,\n home: string,\n which: (bin: string) => boolean,\n): HarnessPlan[] {\n const claudeDir = join(home, '.claude', 'skills');\n const sharedDir = join(home, '.agents', 'skills');\n\n if (override !== undefined && override.length > 0) {\n const plans = override.map((v) =>\n planFor(validateHarness(v), ['override'], true, home, claudeDir, sharedDir),\n );\n return dedupeBySkillsDir(plans);\n }\n\n const plans: HarnessPlan[] = [];\n const claudeBy = detectReasons(existsSync(join(home, '.claude')), which('claude'));\n const codexBy = detectReasons(existsSync(join(home, '.codex')), which('codex'));\n if (claudeBy.length > 0)\n plans.push(planFor('claude', claudeBy, true, home, claudeDir, sharedDir));\n if (codexBy.length > 0) plans.push(planFor('codex', codexBy, true, home, claudeDir, sharedDir));\n if (plans.length === 0) {\n // Nothing detected: the shared Agent Skills location is the fallback target, so\n // a harness installed later still finds the skills.\n plans.push(planFor('shared', ['fallback'], false, home, claudeDir, sharedDir));\n }\n return dedupeBySkillsDir(plans);\n}\n\nfunction detectReasons(homeDirPresent: boolean, onPathPresent: boolean): string[] {\n const reasons: string[] = [];\n if (homeDirPresent) reasons.push('home-dir');\n if (onPathPresent) reasons.push('binary');\n return reasons;\n}\n\nfunction planFor(\n harness: Harness,\n detectedBy: string[],\n detected: boolean,\n home: string,\n claudeDir: string,\n sharedDir: string,\n): HarnessPlan {\n const skillsDir = harness === 'claude' ? claudeDir : sharedDir;\n return { harness, detected, detectedBy, skillsDir, wiresAgentsMd: harness !== 'claude', home };\n}\n\nfunction dedupeBySkillsDir(plans: HarnessPlan[]): HarnessPlan[] {\n const seen = new Set<string>();\n const out: HarnessPlan[] = [];\n for (const p of plans) {\n if (seen.has(p.skillsDir)) continue;\n seen.add(p.skillsDir);\n out.push(p);\n }\n return out;\n}\n\nfunction validateHarness(value: string): Harness {\n if ((HARNESSES as readonly string[]).includes(value)) return value as Harness;\n throw new CliError('USAGE', `Unknown harness \"${value}\"`, {\n fix: `--harness must be one of: ${HARNESSES.join(', ')}.`,\n });\n}\n\n// --- Applying a plan -------------------------------------------------------------\n\nasync function applyPlan(\n plan: HarnessPlan,\n skillsSource: string,\n dryRun: boolean,\n): Promise<HarnessResult> {\n const skills: SkillResult[] = [];\n const warnings: string[] = [];\n for (const name of SKILL_NAMES) {\n const { status, warning } = await installSkill(\n join(skillsSource, name),\n join(plan.skillsDir, name),\n dryRun,\n );\n skills.push({ name, status });\n if (warning !== undefined) warnings.push(warning);\n }\n\n const result: HarnessResult = {\n harness: plan.harness,\n detected: plan.detected,\n detectedBy: plan.detectedBy,\n skillsDir: plan.skillsDir,\n skills,\n notes: notesFor(plan),\n warnings,\n };\n\n if (plan.wiresAgentsMd) {\n result.agentsMd = await wireAgentsMd(plan, dryRun);\n result.codexNetworkRule = CODEX_NETWORK_RULE;\n }\n return result;\n}\n\nfunction notesFor(plan: HarnessPlan): string[] {\n if (plan.harness === 'claude') {\n return [\n 'Installed at the user level (~/.claude/skills). The Claude Code plugin marketplace (roadmap C3) will supersede this path for Claude users.',\n ];\n }\n return [\n 'Copied into the shared Agent Skills location (~/.agents/skills). Codex and any Agent-Skills-compatible harness read it there.',\n ];\n}\n\n/**\n * Copy one skill directory. The packaged copy is canonical: an absent target is\n * `installed`, an identical target is `up-to-date`, and any local drift is\n * overwritten and reported as `updated` with a warning. On --dry-run nothing is\n * written and the status reads `would-*`.\n */\nasync function installSkill(\n srcDir: string,\n destDir: string,\n dryRun: boolean,\n): Promise<{ status: SkillResult['status']; warning?: string }> {\n const src = await readTree(srcDir);\n const dest = await readTree(destDir);\n if (src === null) {\n // assertSkillsSource already guards SKILL.md; this is defensive for an empty dir.\n throw new CliError('INTERNAL', `Packaged skill source ${srcDir} is empty`);\n }\n\n const change = dest === null ? 'create' : treesEqual(src, dest) ? 'none' : 'update';\n\n if (!dryRun && change !== 'none') {\n // Overwrite wholesale so the packaged copy is exactly what lands, with no stray\n // local files left behind. rm is a no-op when the dir is absent.\n await rm(destDir, { recursive: true, force: true });\n for (const [rel, content] of src) {\n await writeFileAtomic(join(destDir, rel), content);\n }\n }\n\n if (change === 'create') return { status: dryRun ? 'would-install' : 'installed' };\n if (change === 'none') return { status: 'up-to-date' };\n return {\n status: dryRun ? 'would-update' : 'updated',\n warning: `${destDir}: local skill copy differed and was ${dryRun ? 'would be ' : ''}overwritten (the packaged copy is canonical).`,\n };\n}\n\n/**\n * Read a directory tree into a rel-path -> content map, or null when it does not\n * exist. Reads as raw `Buffer`, not `utf8`: today's skills are markdown-only, but\n * this is a general recursive dir-copy, and decoding to a string here would\n * silently corrupt a future non-text asset (an image, a font) on write, or worse,\n * make two different corrupted binaries both decode to U+FFFD and compare equal.\n */\nasync function readTree(dir: string): Promise<Map<string, Buffer> | null> {\n if (!existsSync(dir)) return null;\n const entries = await readdir(dir, { recursive: true, withFileTypes: true });\n const files = new Map<string, Buffer>();\n for (const entry of entries) {\n if (!entry.isFile()) continue;\n const full = join(entry.parentPath, entry.name);\n files.set(relative(dir, full), await readFile(full));\n }\n return files;\n}\n\nfunction treesEqual(a: Map<string, Buffer>, b: Map<string, Buffer> | null): boolean {\n if (b === null) return false;\n if (a.size !== b.size) return false;\n for (const [k, v] of a) {\n const other = b.get(k);\n if (other === undefined || !v.equals(other)) return false;\n }\n return true;\n}\n\n/**\n * Ensure the AGENTS.md pointer line is present exactly once. Append-once is GLOBAL\n * across both locations Codex/harnesses read (~/.agents/AGENTS.md and Codex's own\n * ~/.codex/AGENTS.md): if either already carries the marker we stop, so a pointer\n * already in ~/.codex/AGENTS.md is never duplicated into a later-created\n * ~/.agents/AGENTS.md. When neither has it, target selection follows what Codex\n * actually reads: prefer an existing ~/.agents/AGENTS.md, else an existing\n * ~/.codex/AGENTS.md, else create the one whose home dir exists, else fall back to\n * ~/.agents/AGENTS.md alongside the shared skills.\n */\nasync function wireAgentsMd(plan: HarnessPlan, dryRun: boolean): Promise<AgentsMdResult> {\n const shared = join(plan.home, '.agents', 'AGENTS.md');\n const codex = join(plan.home, '.codex', 'AGENTS.md');\n const line = `${AGENTS_MARKER} Tenjin agent skills are installed at ${plan.skillsDir} (tenjin-search, tenjin-publish, tenjin). Read the relevant SKILL.md before using the tenjin CLI.`;\n\n for (const path of [shared, codex]) {\n if (existsSync(path) && (await readFile(path, 'utf8')).includes(AGENTS_MARKER)) {\n return { path, status: 'already-present' };\n }\n }\n\n const path = chooseAgentsMdPath(plan.home);\n if (dryRun) return { path, status: 'would-append' };\n\n const existing = existsSync(path) ? await readFile(path, 'utf8') : null;\n const prefix = existing === null || existing.length === 0 || existing.endsWith('\\n') ? '' : '\\n';\n await writeFileAtomic(path, `${existing ?? ''}${prefix}${line}\\n`);\n return { path, status: 'appended' };\n}\n\nfunction chooseAgentsMdPath(home: string): string {\n const shared = join(home, '.agents', 'AGENTS.md');\n const codex = join(home, '.codex', 'AGENTS.md');\n if (existsSync(shared)) return shared;\n if (existsSync(codex)) return codex;\n if (existsSync(join(home, '.codex'))) return codex;\n return shared;\n}\n\n// --- PATH probe ------------------------------------------------------------------\n\n/**\n * Is `bin` a real executable file on PATH? Gates on statSync().isFile() so a\n * same-named DIRECTORY on PATH never false-positives as the binary, and probes the\n * PATHEXT extensions on win32 where the real binary is `claude.cmd`/`claude.exe`\n * rather than a bare `claude`.\n */\nfunction onPath(bin: string, env: NodeJS.ProcessEnv): boolean {\n const raw = env.PATH ?? '';\n const exts =\n process.platform === 'win32'\n ? ['', ...(env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').filter((e) => e.length > 0)]\n : [''];\n for (const part of raw.split(delimiter)) {\n if (part.length === 0) continue;\n for (const ext of exts) {\n if (isFile(join(part, bin + ext))) return true;\n }\n }\n return false;\n}\n\nfunction isFile(p: string): boolean {\n try {\n return statSync(p).isFile();\n } catch {\n return false;\n }\n}\n\n// --- Human rendering -------------------------------------------------------------\n\nfunction renderHuman(\n io: Io,\n harnesses: HarnessResult[],\n doctor: DoctorChecks,\n dryRun: boolean,\n): string[] {\n const lines: string[] = [];\n if (dryRun) lines.push(paint(io, 'yellow', 'dry run: no files written'));\n for (const h of harnesses) {\n const how = h.detectedBy.length > 0 ? ` (${h.detectedBy.join(', ')})` : '';\n lines.push(paint(io, 'bold', `${h.harness}${how} -> ${h.skillsDir}`));\n for (const s of h.skills) {\n lines.push(` ${skillIcon(io, s.status)} ${s.name.padEnd(14)} ${paint(io, 'dim', s.status)}`);\n }\n if (h.agentsMd !== undefined) {\n lines.push(` ${paint(io, 'dim', `AGENTS.md ${h.agentsMd.status}: ${h.agentsMd.path}`)}`);\n }\n for (const w of h.warnings) lines.push(` ${paint(io, 'yellow', `! ${w}`)}`);\n for (const n of h.notes) lines.push(` ${paint(io, 'dim', n)}`);\n if (h.codexNetworkRule !== undefined) {\n lines.push(\n paint(\n io,\n 'dim',\n \" Codex's default workspace-write sandbox blocks network, which breaks paid x402 calls.\",\n ),\n );\n lines.push(paint(io, 'dim', ' Add this to ~/.codex/config.toml:'));\n for (const rl of h.codexNetworkRule.split('\\n')) lines.push(` ${rl}`);\n }\n }\n lines.push('');\n lines.push(paint(io, 'bold', 'doctor (final checks):'));\n lines.push(...renderDoctorHuman(io, doctor.checks));\n if (doctor.failure !== undefined) {\n lines.push(paint(io, 'yellow', 'doctor reported a problem above; the skills are still wired.'));\n }\n return lines;\n}\n\nfunction skillIcon(io: Io, status: SkillResult['status']): string {\n if (status === 'up-to-date') return paint(io, 'green', '=');\n if (status === 'updated' || status === 'would-update') return paint(io, 'yellow', '~');\n return paint(io, 'green', '+');\n}\n\nfunction paint(io: Io, format: Parameters<typeof styleText>[0], text: string): string {\n if (io.stderr instanceof Stream) return styleText(format, text, { stream: io.stderr });\n return styleText(format, text);\n}\n\n// --- Skills source guard ---------------------------------------------------------\n\nasync function assertSkillsSource(dir: string): Promise<void> {\n for (const name of SKILL_NAMES) {\n if (!existsSync(join(dir, name, 'SKILL.md'))) {\n throw new CliError('INTERNAL', `Packaged skill \"${name}\" is missing under ${dir}`, {\n fix: 'Reinstall tenjin-cli; the published package must ship every skill under skills/.',\n });\n }\n }\n}\n","import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { CliError } from './errors';\n\n/**\n * The packaged skills `install` copies into every target harness. All three go to\n * every target: `tenjin` (the zero-install curriculum, works with no CLI),\n * `tenjin-search` and `tenjin-publish` (thin CLI adapters). Order is the install\n * order and the human-render order.\n */\nexport const SKILL_NAMES = ['tenjin-search', 'tenjin-publish', 'tenjin'] as const;\nexport type SkillName = (typeof SKILL_NAMES)[number];\n\n// A `skills/` directory is the real one iff it holds `tenjin/SKILL.md`; a bare\n// `skills/` created for some other reason up the tree can't false-positive.\nconst SENTINEL = join('tenjin', 'SKILL.md');\n\n/**\n * Locate the packaged `skills/` directory by walking up from `startDir`. This has\n * to work in two layouts from one resolver: a global npm install where the module\n * lives in `dist/` with `skills/` a sibling (the `files` array ships it), and a\n * repo/source run where the module is under `src/` and `skills/` sits at the repo\n * root. Walking up until a `skills/` with the sentinel appears covers both without\n * hard-coding either depth. Sync (existsSync) on purpose: this is one-shot path\n * resolution at command start, the same shape as `require.resolve`.\n */\nexport function resolveSkillsSource(startDir: string): string {\n let dir = startDir;\n for (let i = 0; i < 8; i += 1) {\n const candidate = join(dir, 'skills');\n if (existsSync(join(candidate, SENTINEL))) return candidate;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n throw new CliError('INTERNAL', 'Could not locate the packaged Tenjin skills directory', {\n fix: 'Reinstall tenjin-cli; the published package must ship the skills/ directory (files array).',\n details: { startDir },\n });\n}\n"],"mappings":"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","names":["existsSync","join","join","plans","existsSync","path"]}
|
|
@@ -8,19 +8,23 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
buildLookupRequest,
|
|
10
10
|
postLookup
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-662MSGZB.js";
|
|
12
|
+
import {
|
|
13
|
+
listCandidates
|
|
14
|
+
} from "./chunk-TLCMMYLY.js";
|
|
12
15
|
import "./chunk-3OV7E5G5.js";
|
|
13
16
|
import {
|
|
14
17
|
sanitizeForTerminal
|
|
15
18
|
} from "./chunk-Z52RA3NC.js";
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-4ONXEEQL.js";
|
|
20
|
+
import "./chunk-FJPLRISB.js";
|
|
18
21
|
import {
|
|
19
22
|
resolveContextSettings
|
|
20
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-GO75V3NZ.js";
|
|
21
24
|
import {
|
|
22
25
|
parseUsdToAtomic
|
|
23
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-ESPNTPQ2.js";
|
|
27
|
+
import "./chunk-2I4QTOLB.js";
|
|
24
28
|
import "./chunk-YBMMV4Y4.js";
|
|
25
29
|
import "./chunk-UO22YNY4.js";
|
|
26
30
|
import "./chunk-MUBUQVC3.js";
|
|
@@ -70,6 +74,7 @@ async function runLookup(args, ctx, deps = {}) {
|
|
|
70
74
|
price: c.price
|
|
71
75
|
}))
|
|
72
76
|
});
|
|
77
|
+
if (response.decision === "MISS") await emitCandidateNudge(ctx);
|
|
73
78
|
const humanLines = response.decision === "MISS" ? [`MISS, no candidates (lookupId ${response.lookupId})`] : [
|
|
74
79
|
`${candidates.length} candidate(s) (lookupId ${response.lookupId}):`,
|
|
75
80
|
...candidates.map(
|
|
@@ -78,6 +83,17 @@ async function runLookup(args, ctx, deps = {}) {
|
|
|
78
83
|
];
|
|
79
84
|
return { data: response, humanLines };
|
|
80
85
|
}
|
|
86
|
+
var STALE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
87
|
+
async function emitCandidateNudge(ctx) {
|
|
88
|
+
const records = await listCandidates(ctx.dataDir);
|
|
89
|
+
if (records.length === 0) return;
|
|
90
|
+
const now = Date.now();
|
|
91
|
+
const stale = records.filter((r) => now - Date.parse(r.meta.created) > STALE_MS).length;
|
|
92
|
+
ctx.io.stderr.write(
|
|
93
|
+
`${records.length} candidate(s) parked (${stale} stale >7d) - tenjin candidate list
|
|
94
|
+
`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
81
97
|
function parseLimit(raw) {
|
|
82
98
|
const n = Number(raw);
|
|
83
99
|
if (!Number.isInteger(n)) {
|
|
@@ -110,4 +126,4 @@ function parseAppliesTo(pairs) {
|
|
|
110
126
|
export {
|
|
111
127
|
runLookup
|
|
112
128
|
};
|
|
113
|
-
//# sourceMappingURL=lookup-
|
|
129
|
+
//# sourceMappingURL=lookup-ZPN2Q67N.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/lookup.ts"],"sourcesContent":["import { CliError } from '../lib/errors';\nimport { parseUsdToAtomic } from '../lib/money';\nimport { resolveContextSettings } from '../lib/settings';\nimport { buildLookupRequest, postLookup, type LookupInput } from '../lib/agent-api';\nimport { recordLookup } from '../lib/lookup-store';\nimport { listCandidates } from '../lib/candidate-store';\nimport { assertOnBaseOrigin } from '../lib/resource-ref';\nimport { sanitizeForTerminal } from '../lib/output';\nimport type { CommandContext, CommandResult } from '../context';\n\n/**\n * `tenjin lookup \"<question>\"`, one POST to /api/agent/lookup. Prints the compact\n * CANDIDATES/MISS response verbatim (spec 10), and records the lookupId +\n * candidates locally so `outcome --last` and `buy <resourceId>` can use them. No\n * wallet, no signing: lookup is anonymous.\n */\n\nexport interface LookupArgs {\n question: string;\n /** Decimal USD at the edge (O1); converted to atomic for the wire. */\n maxPrice?: string;\n freshWithin?: string;\n limit?: string;\n /** Raw `k=v` / `k=v1,v2` pairs from repeated --applies-to. */\n appliesTo?: string[];\n}\n\nexport interface LookupDeps {\n fetchImpl?: typeof fetch;\n}\n\nexport async function runLookup(\n args: LookupArgs,\n ctx: CommandContext,\n deps: LookupDeps = {},\n): Promise<CommandResult> {\n const settings = await resolveContextSettings(ctx);\n const input: LookupInput = { question: args.question };\n if (args.maxPrice !== undefined) input.maxPrice = parseUsdToAtomic(args.maxPrice);\n if (args.freshWithin !== undefined) input.freshWithin = args.freshWithin;\n if (args.limit !== undefined) input.limit = parseLimit(args.limit);\n if (args.appliesTo !== undefined && args.appliesTo.length > 0) {\n input.appliesTo = parseAppliesTo(args.appliesTo);\n }\n\n const request = buildLookupRequest(input);\n const response = await postLookup(request, {\n baseUrl: settings.baseUrl,\n timeoutMs: ctx.flags.timeout,\n evalCohort: settings.evalCohort,\n ...(deps.fetchImpl !== undefined ? { fetchImpl: deps.fetchImpl } : {}),\n });\n\n const candidates = response.candidates ?? [];\n // Ingest trust boundary: a candidate url that points off the configured origin\n // would later route a wallet-signed SIWX header and payment to that host via\n // `buy <resourceId>`. Refuse the whole response as a contract violation.\n for (const c of candidates) {\n try {\n assertOnBaseOrigin(c.url, settings.baseUrl, 'lookup candidate URL');\n } catch (err) {\n throw new CliError(\n 'CONTRACT_MISMATCH',\n `Lookup candidate ${c.resourceId} points off the configured base URL.`,\n { cause: err },\n );\n }\n }\n await recordLookup(ctx.dataDir, {\n lookupId: response.lookupId,\n at: new Date().toISOString(),\n question: request.question,\n decision: response.decision,\n candidates: candidates.map((c) => ({\n resourceId: c.resourceId,\n url: c.url,\n title: c.title,\n price: c.price,\n })),\n });\n\n // A parked-candidate nudge on stderr (not in the machine JSON), MISS only: a\n // MISS is the moment to publish the answer you are about to derive, and stale\n // drafts should not rot unseen. A HIT is not a publish moment, and hot lookup\n // paths should not get advisory noise every call. One line, only when parked.\n if (response.decision === 'MISS') await emitCandidateNudge(ctx);\n\n const humanLines =\n response.decision === 'MISS'\n ? [`MISS, no candidates (lookupId ${response.lookupId})`]\n : [\n `${candidates.length} candidate(s) (lookupId ${response.lookupId}):`,\n ...candidates.map(\n (c, i) =>\n ` ${i + 1}. ${sanitizeForTerminal(c.title)}, ${c.price} atomic, ${sanitizeForTerminal(c.url)}`,\n ),\n ];\n\n return { data: response, humanLines };\n}\n\nconst STALE_MS = 7 * 24 * 60 * 60 * 1000;\n\n/** One stderr line naming parked candidates (and how many are stale >7d), so a\n * lookup is a reminder to publish/tidy the local pen. Silent when none parked. */\nasync function emitCandidateNudge(ctx: CommandContext): Promise<void> {\n const records = await listCandidates(ctx.dataDir);\n if (records.length === 0) return;\n const now = Date.now();\n const stale = records.filter((r) => now - Date.parse(r.meta.created) > STALE_MS).length;\n ctx.io.stderr.write(\n `${records.length} candidate(s) parked (${stale} stale >7d) - tenjin candidate list\\n`,\n );\n}\n\nfunction parseLimit(raw: string): number {\n const n = Number(raw);\n if (!Number.isInteger(n)) {\n throw new CliError('USAGE', `Invalid --limit: ${JSON.stringify(raw)}`, {\n fix: 'Pass an integer between 1 and 10.',\n });\n }\n return n;\n}\n\n/** `products=Vercel` or `products=Vercel,Next` → { products: [\"Vercel\",\"Next\"] }. Repeated\n * keys merge their values. */\nfunction parseAppliesTo(pairs: string[]): Record<string, string[]> {\n const out: Record<string, string[]> = {};\n for (const pair of pairs) {\n const eq = pair.indexOf('=');\n if (eq <= 0) {\n throw new CliError('USAGE', `Invalid --applies-to: ${JSON.stringify(pair)}`, {\n fix: 'Use key=value, e.g. --applies-to products=Vercel.',\n });\n }\n const key = pair.slice(0, eq).trim();\n const values = pair\n .slice(eq + 1)\n .split(',')\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n if (values.length === 0) {\n throw new CliError('USAGE', `--applies-to ${JSON.stringify(key)} has no values`, {\n fix: 'Give each key at least one value, e.g. products=Vercel.',\n });\n }\n out[key] = [...(out[key] ?? []), ...values];\n }\n return out;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -5,14 +5,14 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
buildOutcomeItem,
|
|
7
7
|
postOutcomes
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-662MSGZB.js";
|
|
9
9
|
import "./chunk-3OV7E5G5.js";
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-4ONXEEQL.js";
|
|
11
|
+
import "./chunk-FJPLRISB.js";
|
|
12
12
|
import {
|
|
13
13
|
resolveContextSettings
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-GO75V3NZ.js";
|
|
15
|
+
import "./chunk-ESPNTPQ2.js";
|
|
16
16
|
import "./chunk-YBMMV4Y4.js";
|
|
17
17
|
import "./chunk-UO22YNY4.js";
|
|
18
18
|
import "./chunk-MUBUQVC3.js";
|
|
@@ -63,4 +63,4 @@ async function resolveLookupId(args, ctx) {
|
|
|
63
63
|
export {
|
|
64
64
|
runOutcome
|
|
65
65
|
};
|
|
66
|
-
//# sourceMappingURL=outcome-
|
|
66
|
+
//# sourceMappingURL=outcome-N7TAK6WR.js.map
|
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
buildSiwxHeader,
|
|
4
4
|
headingOutline
|
|
5
5
|
} from "./chunk-S3IGX57D.js";
|
|
6
|
+
import {
|
|
7
|
+
rateLimitError
|
|
8
|
+
} from "./chunk-662MSGZB.js";
|
|
6
9
|
import {
|
|
7
10
|
dropCandidate,
|
|
8
11
|
readCandidate
|
|
9
12
|
} from "./chunk-TLCMMYLY.js";
|
|
10
|
-
import {
|
|
11
|
-
rateLimitError
|
|
12
|
-
} from "./chunk-YIYQ2ZNT.js";
|
|
13
13
|
import {
|
|
14
14
|
ATOMIC_RE,
|
|
15
15
|
UUID_RE
|
|
@@ -21,8 +21,8 @@ import {
|
|
|
21
21
|
CLIENT_HEADER,
|
|
22
22
|
httpRequest,
|
|
23
23
|
trimSlash
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import "./chunk-
|
|
24
|
+
} from "./chunk-4ONXEEQL.js";
|
|
25
|
+
import "./chunk-FJPLRISB.js";
|
|
26
26
|
import "./chunk-RRN3YTK7.js";
|
|
27
27
|
import "./chunk-CYOF7ROH.js";
|
|
28
28
|
import "./chunk-ZABPQJ3M.js";
|
|
@@ -30,14 +30,13 @@ import "./chunk-HSDCI7OV.js";
|
|
|
30
30
|
import {
|
|
31
31
|
resolveContextSettings,
|
|
32
32
|
resolvePublishSettings
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-GO75V3NZ.js";
|
|
34
34
|
import {
|
|
35
35
|
PublishModeSchema,
|
|
36
|
-
formatUsdDisplay,
|
|
37
36
|
parsePublishModeFlag,
|
|
38
37
|
parseUsdToAtomic,
|
|
39
38
|
toMoney
|
|
40
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-ESPNTPQ2.js";
|
|
41
40
|
import {
|
|
42
41
|
describeWallet,
|
|
43
42
|
resolveWalletProvider
|
|
@@ -193,9 +192,9 @@ var LINE_DETECTORS = [
|
|
|
193
192
|
skip: (m) => isPlaceholder(m[1] ?? "")
|
|
194
193
|
},
|
|
195
194
|
// Generic secret-named assignment — WARN, not block (review): a keyword match is
|
|
196
|
-
// lower-confidence than a structured shape, and warn still forces confirmation
|
|
197
|
-
// in
|
|
198
|
-
// (SECRET_NAME=…, MYSQL_ROOT_PASSWORD=…) is not permanently non-bypassable. The
|
|
195
|
+
// lower-confidence than a structured shape, and a warn still forces confirmation
|
|
196
|
+
// in auto mode (and review always asks), so nothing publishes unseen while benign
|
|
197
|
+
// config (SECRET_NAME=…, MYSQL_ROOT_PASSWORD=…) is not permanently non-bypassable. The
|
|
199
198
|
// excerpt stays masked. Placeholder and structural (path/URL/regex) values are
|
|
200
199
|
// skipped entirely.
|
|
201
200
|
{
|
|
@@ -1212,9 +1211,8 @@ async function runPublish(args, ctx, deps = {}) {
|
|
|
1212
1211
|
);
|
|
1213
1212
|
}
|
|
1214
1213
|
if (settings.modeSource === "default") {
|
|
1215
|
-
const usd = formatUsdDisplay(settings.defaultPriceAtomic);
|
|
1216
1214
|
ctx.io.stderr.write(
|
|
1217
|
-
`publish.mode: ${settings.mode} (default) -
|
|
1215
|
+
`publish.mode: ${settings.mode} (default) - each publish asks you once. Set auto to publish clean scans automatically: tenjin config set publish.mode auto.
|
|
1218
1216
|
`
|
|
1219
1217
|
);
|
|
1220
1218
|
}
|
|
@@ -1477,4 +1475,4 @@ function confirmMessage(warnCount, priceUsd) {
|
|
|
1477
1475
|
export {
|
|
1478
1476
|
runPublish
|
|
1479
1477
|
};
|
|
1480
|
-
//# sourceMappingURL=publish-
|
|
1478
|
+
//# sourceMappingURL=publish-BKBQQAW4.js.map
|