@precisa-saude/cli 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/bin.js +41 -12
  2. package/dist/bin.js.map +1 -1
  3. package/dist/index.d.ts +8 -1
  4. package/dist/index.js +28 -9
  5. package/dist/index.js.map +1 -1
  6. package/dist/templates/.claude/agents/debugger.md +130 -0
  7. package/dist/templates/.claude/agents/docs-sync.md +97 -0
  8. package/dist/templates/.claude/agents/pr-review-responder.md +117 -0
  9. package/dist/templates/.claude/agents/pre-commit-check.md +85 -0
  10. package/dist/templates/.claude/agents/refactor-scout.md +139 -0
  11. package/dist/templates/AGENTS.md +45 -0
  12. package/dist/templates/CLAUDE.md +9 -0
  13. package/dist/templates/commitlintrc.cjs +10 -0
  14. package/dist/templates/eslint.config.js +19 -0
  15. package/dist/templates/github/ISSUE_TEMPLATE/bug_report.md +33 -0
  16. package/dist/templates/github/ISSUE_TEMPLATE/config.yml +4 -4
  17. package/dist/templates/github/ISSUE_TEMPLATE/feature_request.md +18 -0
  18. package/dist/templates/github/PULL_REQUEST_TEMPLATE.md +10 -10
  19. package/dist/templates/github/workflows/_checks.yml +102 -0
  20. package/dist/templates/github/workflows/_deploy-site.yml +168 -0
  21. package/dist/templates/github/workflows/_publish.yml +77 -0
  22. package/dist/templates/github/workflows/_release.yml +156 -0
  23. package/dist/templates/github/workflows/ci.yml +63 -27
  24. package/dist/templates/github/workflows/doctor.yml +71 -0
  25. package/dist/templates/github/workflows/publish-tag.yml +71 -0
  26. package/dist/templates/github/workflows/release.yml +22 -8
  27. package/dist/templates/github/workflows/review.yml +75 -23
  28. package/dist/templates/governance/CITATION.cff +1 -1
  29. package/dist/templates/governance/CONTRIBUTING.md +14 -14
  30. package/dist/templates/governance/CONVENTIONS.md +21 -19
  31. package/dist/templates/governance/SECURITY.md +13 -13
  32. package/dist/templates/governance/SUPPORT.md +10 -10
  33. package/dist/templates/husky/commit-msg +4 -2
  34. package/dist/templates/husky/pre-commit +10 -0
  35. package/dist/templates/husky/pre-push +24 -10
  36. package/dist/templates/package.json +71 -0
  37. package/dist/templates/pnpm-workspace.yaml +3 -0
  38. package/dist/templates/scripts/worktree.sh +5 -0
  39. package/dist/templates/templates.manifest.yml +127 -7
  40. package/dist/templates/tsconfig.json +4 -0
  41. package/dist/templates/turbo.json +32 -0
  42. package/package.json +2 -2
  43. package/dist/templates/github/ISSUE_TEMPLATE/bug.md +0 -33
  44. package/dist/templates/github/ISSUE_TEMPLATE/feature.md +0 -18
package/dist/bin.js CHANGED
@@ -78,6 +78,8 @@ function isRequired(when, manifest) {
78
78
  switch (when) {
79
79
  case "always":
80
80
  return true;
81
+ case "never":
82
+ return false;
81
83
  case "oss":
82
84
  return manifest.visibility === "oss";
83
85
  case "private":
@@ -95,6 +97,11 @@ function isRequired(when, manifest) {
95
97
  function tokenContext(manifest) {
96
98
  return {
97
99
  COMMIT_SCOPES: manifest.commitScopes.join(","),
100
+ // For human-readable docs (AGENTS.md, README) — backticked list.
101
+ COMMIT_SCOPES_HUMAN: manifest.commitScopes.map((s) => `\`${s}\``).join(", "),
102
+ // For `.commitlintrc.cjs` — scope array spelled as JS string literals,
103
+ // ready to drop inside `[ ... ]`.
104
+ COMMIT_SCOPES_JSON: manifest.commitScopes.map((s) => `'${s}'`).join(", "),
98
105
  CONDUCT_EMAIL: manifest.contactEmails.conduct,
99
106
  HAS_PACKAGES: String(manifest.hasPackages),
100
107
  HAS_SITE: String(manifest.hasSite),
@@ -230,15 +237,22 @@ ${err.message}`));
230
237
  if (errors > 0) process.exit(1);
231
238
  }
232
239
  function compareContent(entry, current, rendered) {
240
+ if (current === rendered) {
241
+ return { message: "", severity: "ok", target: entry.target };
242
+ }
233
243
  if (entry.merge_strategy === "preserve") {
234
244
  return {
235
- message: current === rendered ? "" : "Differs from template (preserve strategy \u2014 suggestion only)",
236
- severity: current === rendered ? "ok" : "info",
245
+ message: "Differs from template (preserve strategy \u2014 suggestion only)",
246
+ severity: "info",
237
247
  target: entry.target
238
248
  };
239
249
  }
240
- if (current === rendered) {
241
- return { message: "", severity: "ok", target: entry.target };
250
+ if (entry.merge_strategy === "skip_if_exists") {
251
+ return {
252
+ message: "Differs from template (scaffold-only; sync will not overwrite)",
253
+ severity: "info",
254
+ target: entry.target
255
+ };
242
256
  }
243
257
  return {
244
258
  message: "Differs from template (run `precisa sync` to update)",
@@ -391,28 +405,33 @@ precisa new ${repoName}`));
391
405
  console.log(chalk2.dim(`profile: ${opts.profile}${opts.dryRun ? " (dry-run)" : ""}`));
392
406
  console.log(chalk2.dim(`target: ${targetDir}
393
407
  `));
394
- const answers = await prompts(
408
+ const answers = opts.nonInteractive ? {
409
+ commitScopes: (opts.scopes ?? "docs,ci,deps").split(",").map((s) => s.trim()).filter(Boolean),
410
+ conductEmail: opts.conductEmail ?? "conduct@precisa-saude.com.br",
411
+ owner: opts.owner ?? "Precisa-Saude",
412
+ securityEmail: opts.securityEmail ?? "security@precisa-saude.com.br"
413
+ } : await prompts(
395
414
  [
396
415
  {
397
- initial: "Precisa-Saude",
416
+ initial: opts.owner ?? "Precisa-Saude",
398
417
  message: "GitHub owner (org or user):",
399
418
  name: "owner",
400
419
  type: "text"
401
420
  },
402
421
  {
403
- initial: "security@precisa-saude.com.br",
422
+ initial: opts.securityEmail ?? "security@precisa-saude.com.br",
404
423
  message: "Security contact email:",
405
424
  name: "securityEmail",
406
425
  type: "text"
407
426
  },
408
427
  {
409
- initial: "conduct@precisa-saude.com.br",
428
+ initial: opts.conductEmail ?? "conduct@precisa-saude.com.br",
410
429
  message: "Code-of-conduct contact email:",
411
430
  name: "conductEmail",
412
431
  type: "text"
413
432
  },
414
433
  {
415
- initial: "docs,ci,deps",
434
+ initial: opts.scopes ?? "docs,ci,deps",
416
435
  message: "Commit scopes (comma-separated):",
417
436
  name: "commitScopes",
418
437
  separator: ",",
@@ -624,9 +643,19 @@ function indent(text) {
624
643
  // src/bin.ts
625
644
  var program = new Command();
626
645
  program.name("precisa").description("Bootstrap and sync Precisa Sa\xFAde repositories.").version("0.0.0");
627
- program.command("new <repo-name>").description("Scaffold a new repository from the shared templates.").option("--profile <profile>", "preset: oss-library | oss-site | private-app", "oss-library").option("--dry-run", "print what would be written without touching disk", false).action(async (repoName, opts) => {
628
- await runNew(repoName, opts);
629
- });
646
+ program.command("new <repo-name>").description("Scaffold a new repository from the shared templates.").option("--profile <profile>", "preset: oss-library | oss-site | private-app", "oss-library").option("--dry-run", "print what would be written without touching disk", false).option("--non-interactive", "skip prompts; use --owner/--scopes/etc. flags instead", false).option("--owner <org>", "GitHub owner (org or user)", "Precisa-Saude").option(
647
+ "--scopes <csv>",
648
+ 'Comma-separated commit scopes (e.g. "core,docs,ci,deps")',
649
+ "docs,ci,deps"
650
+ ).option("--security-email <email>", "Security contact email", "security@precisa-saude.com.br").option(
651
+ "--conduct-email <email>",
652
+ "Code-of-conduct contact email",
653
+ "conduct@precisa-saude.com.br"
654
+ ).action(
655
+ async (repoName, opts) => {
656
+ await runNew(repoName, opts);
657
+ }
658
+ );
630
659
  program.command("sync").description("Re-render templates against this repo's .precisa.json manifest.").option("--dry-run", "print a diff without writing", false).action(async (opts) => {
631
660
  await runSync(opts);
632
661
  });
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bin.ts","../src/commands/doctor.ts","../src/lib/templates.ts","../src/lib/paths.ts","../src/manifest.ts","../src/commands/new.ts","../src/lib/merge.ts","../src/commands/sync.ts","../src/lib/diff.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\n\nimport { runDoctor } from './commands/doctor.js';\nimport { runNew } from './commands/new.js';\nimport { runSync } from './commands/sync.js';\n\nconst program = new Command();\n\nprogram\n .name('precisa')\n .description('Bootstrap and sync Precisa Saúde repositories.')\n .version('0.0.0');\n\nprogram\n .command('new <repo-name>')\n .description('Scaffold a new repository from the shared templates.')\n .option('--profile <profile>', 'preset: oss-library | oss-site | private-app', 'oss-library')\n .option('--dry-run', 'print what would be written without touching disk', false)\n .action(async (repoName: string, opts: { profile: string; dryRun: boolean }) => {\n await runNew(repoName, opts);\n });\n\nprogram\n .command('sync')\n .description(\"Re-render templates against this repo's .precisa.json manifest.\")\n .option('--dry-run', 'print a diff without writing', false)\n .action(async (opts: { dryRun: boolean }) => {\n await runSync(opts);\n });\n\nprogram\n .command('doctor')\n .description('Audit this repo against the templates and report drift.')\n .action(async () => {\n await runDoctor();\n });\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n console.error(err);\n process.exit(1);\n});\n","import { existsSync, readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport chalk from 'chalk';\n\nimport {\n loadTemplateManifest,\n readTemplateSource,\n renderTokens,\n type TemplateEntry,\n} from '../lib/templates.js';\nimport { isRequired, loadManifest, tokenContext } from '../manifest.js';\n\ntype Severity = 'ok' | 'info' | 'warning' | 'error';\n\ninterface DriftReport {\n message: string;\n severity: Severity;\n target: string;\n}\n\nexport async function runDoctor(): Promise<void> {\n const cwd = process.cwd();\n console.log(chalk.bold.cyan('\\nprecisa doctor'));\n\n let manifest;\n try {\n manifest = loadManifest(cwd);\n } catch (err) {\n console.error(chalk.red(`\\n${(err as Error).message}`));\n process.exit(1);\n }\n\n const entries = loadTemplateManifest();\n const context = tokenContext(manifest);\n\n const reports: DriftReport[] = [];\n\n for (const entry of entries) {\n const required = isRequired(entry.required_when, manifest);\n const targetPath = resolve(cwd, entry.target);\n const exists = existsSync(targetPath);\n\n if (!required && !exists) continue;\n if (!required && exists) {\n reports.push({\n message: `Present but not required by the manifest profile (ok)`,\n severity: 'info',\n target: entry.target,\n });\n continue;\n }\n if (!exists) {\n reports.push({\n message: 'Missing',\n severity: 'error',\n target: entry.target,\n });\n continue;\n }\n\n // Exists + required — compare to rendered template\n const source = readTemplateSource(entry);\n const rendered = renderTokens(source, context);\n const current = readFileSync(targetPath, 'utf-8');\n reports.push(compareContent(entry, current, rendered));\n }\n\n console.log('');\n let errors = 0;\n let warnings = 0;\n let infos = 0;\n let oks = 0;\n\n for (const r of reports) {\n const icon = iconFor(r.severity);\n const line = `${icon} ${r.target}${r.message ? chalk.dim(` — ${r.message}`) : ''}`;\n console.log(line);\n if (r.severity === 'error') errors += 1;\n else if (r.severity === 'warning') warnings += 1;\n else if (r.severity === 'info') infos += 1;\n else oks += 1;\n }\n\n console.log('');\n console.log(chalk.dim(`${oks} ok, ${warnings} warning, ${errors} error, ${infos} info`));\n\n if (errors > 0) process.exit(1);\n}\n\nfunction compareContent(entry: TemplateEntry, current: string, rendered: string): DriftReport {\n if (entry.merge_strategy === 'preserve') {\n return {\n message:\n current === rendered ? '' : 'Differs from template (preserve strategy — suggestion only)',\n severity: current === rendered ? 'ok' : 'info',\n target: entry.target,\n };\n }\n if (current === rendered) {\n return { message: '', severity: 'ok', target: entry.target };\n }\n return {\n message: 'Differs from template (run `precisa sync` to update)',\n severity: 'warning',\n target: entry.target,\n };\n}\n\nfunction iconFor(severity: Severity): string {\n switch (severity) {\n case 'ok':\n return chalk.green('✓');\n case 'info':\n return chalk.blue('i');\n case 'warning':\n return chalk.yellow('!');\n case 'error':\n return chalk.red('✗');\n }\n}\n","import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { parse as parseYaml } from 'yaml';\n\nimport type { RequiredWhen } from '../manifest.js';\nimport { resolveTemplatesDir } from './paths.js';\n\nexport type MergeStrategy = 'overwrite' | 'merge_json' | 'preserve' | 'skip_if_exists';\n\nexport interface TemplateEntry {\n /** Whether to mark the rendered file executable (chmod +x). */\n executable?: boolean;\n /** How to merge when the target already exists. */\n merge_strategy: MergeStrategy;\n /** Gate for inclusion. */\n required_when: RequiredWhen;\n /** Path relative to templates/. */\n source: string;\n /** Path in the target repo (relative to the repo root). */\n target: string;\n}\n\ninterface TemplateManifestFile {\n templates: TemplateEntry[];\n}\n\n/**\n * Load the `templates.manifest.yml` from the bundled templates directory.\n * Throws if the file is missing or the shape is wrong.\n */\nexport function loadTemplateManifest(): TemplateEntry[] {\n const dir = resolveTemplatesDir();\n const manifestPath = resolve(dir, 'templates.manifest.yml');\n const raw = readFileSync(manifestPath, 'utf-8');\n const parsed = parseYaml(raw) as TemplateManifestFile | undefined;\n if (!parsed || !Array.isArray(parsed.templates)) {\n throw new Error(`${manifestPath}: expected { templates: [...] } at root`);\n }\n for (const [i, t] of parsed.templates.entries()) {\n if (!t.source || !t.target) {\n throw new Error(`${manifestPath}: entry ${i} is missing source or target`);\n }\n }\n return parsed.templates;\n}\n\nexport function readTemplateSource(entry: TemplateEntry): string {\n const dir = resolveTemplatesDir();\n const path = resolve(dir, entry.source);\n return readFileSync(path, 'utf-8');\n}\n\n/**\n * Substitute `{{TOKEN}}` placeholders with values from `context`. Unknown\n * tokens are left untouched (keeps the double braces visible so bad keys\n * are easy to spot in rendered output).\n */\nexport function renderTokens(source: string, context: Record<string, string>): string {\n return source.replace(/\\{\\{([A-Z_]+)\\}\\}/g, (match, token: string) => {\n return Object.prototype.hasOwnProperty.call(context, token) ? context[token]! : match;\n });\n}\n","import { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Locate the bundled `templates/` directory.\n *\n * When published to npm, the templates live at `dist/templates/` inside\n * the CLI package (copied by `scripts/copy-templates.mjs` during build).\n *\n * When running from the monorepo (e.g. `pnpm --filter @precisa-saude/cli dev`),\n * the templates live at the repo root — fall back to walking up from the\n * package directory.\n */\nexport function resolveTemplatesDir(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n\n // Published case: cli package's dist/templates\n const bundled = resolve(here, 'templates');\n if (existsSync(bundled)) return bundled;\n\n // Dev case: walk up from packages/cli/{src|dist} to the monorepo root\n const repoRoot = resolve(here, '..', '..', '..', '..');\n const repoTemplates = resolve(repoRoot, 'templates');\n if (existsSync(repoTemplates)) return repoTemplates;\n\n // Sibling-in-package case (tsup output): packages/cli/dist -> ../../templates\n const packageParent = resolve(here, '..', '..', 'templates');\n if (existsSync(packageParent)) return packageParent;\n\n throw new Error(\n `Could not locate templates/ directory. Searched:\\n ${bundled}\\n ${repoTemplates}\\n ${packageParent}`,\n );\n}\n","import { readFileSync, writeFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\n/**\n * `.precisa.json` manifest — sits at the root of every consumer repo and\n * declares the repo's profile so `precisa sync` and `precisa doctor` know\n * which templates to render and which rules to enforce.\n */\nexport interface PrecisaManifest {\n /** Commitlint scope enum for this repo. */\n commitScopes: string[];\n\n /** Contact emails used in governance doc templates. */\n contactEmails: {\n security: string;\n conduct: string;\n };\n\n /** Does this repo have publishable packages/*? */\n hasPackages: boolean;\n\n /** Does this repo ship a website? Controls preview-deploy workflows. */\n hasSite: boolean;\n\n /** Repository name (typically matches the GitHub repo name). */\n name: string;\n\n /** Pinned runtime versions. Default: node=22, pnpm=9.15.9. */\n nodeVersion?: string;\n\n /** GitHub organization or user that owns the repo (e.g. `Precisa-Saude`). */\n owner: string;\n\n pnpmVersion?: string;\n\n /** Publishes any workspace package to npm? */\n publishesToNpm: boolean;\n\n /** Schema version of this manifest file. Bump when the schema changes. */\n schemaVersion: 1;\n /** Public-OSS or private-internal. Controls which templates are rendered. */\n visibility: 'oss' | 'private';\n}\n\nexport const MANIFEST_FILENAME = '.precisa.json';\n\nexport const DEFAULT_MANIFEST_FIELDS = {\n hasPackages: true,\n hasSite: false,\n nodeVersion: '22',\n pnpmVersion: '9.15.9',\n publishesToNpm: true,\n schemaVersion: 1 as const,\n visibility: 'oss' as const,\n};\n\n/** Template gate values — `required_when` in templates.manifest.yml. */\nexport type RequiredWhen =\n | 'always'\n | 'oss'\n | 'private'\n | 'has_site'\n | 'has_packages'\n | 'publishes_to_npm';\n\n/** Returns true when a template's `required_when` gate applies to this manifest. */\nexport function isRequired(when: RequiredWhen, manifest: PrecisaManifest): boolean {\n switch (when) {\n case 'always':\n return true;\n case 'oss':\n return manifest.visibility === 'oss';\n case 'private':\n return manifest.visibility === 'private';\n case 'has_site':\n return manifest.hasSite;\n case 'has_packages':\n return manifest.hasPackages;\n case 'publishes_to_npm':\n return manifest.publishesToNpm;\n default:\n return true;\n }\n}\n\n/**\n * Build the token substitution map from a manifest. Keys are `{{TOKEN}}`\n * (without braces); values are always strings.\n */\nexport function tokenContext(manifest: PrecisaManifest): Record<string, string> {\n return {\n COMMIT_SCOPES: manifest.commitScopes.join(','),\n CONDUCT_EMAIL: manifest.contactEmails.conduct,\n HAS_PACKAGES: String(manifest.hasPackages),\n HAS_SITE: String(manifest.hasSite),\n NODE_VERSION: manifest.nodeVersion ?? '22',\n OWNER_ORG: manifest.owner,\n PNPM_VERSION: manifest.pnpmVersion ?? '9.15.9',\n PUBLISHES_TO_NPM: String(manifest.publishesToNpm),\n REPO_NAME: manifest.name,\n REPO_SLUG: `${manifest.owner}/${manifest.name}`,\n SECURITY_EMAIL: manifest.contactEmails.security,\n VISIBILITY: manifest.visibility,\n };\n}\n\nexport interface ManifestValidationError {\n message: string;\n path: string;\n}\n\nexport function validateManifest(raw: unknown): ManifestValidationError[] {\n const errors: ManifestValidationError[] = [];\n const m = raw as Partial<PrecisaManifest> | undefined;\n if (!m || typeof m !== 'object') {\n return [{ message: 'manifest must be a JSON object', path: '$' }];\n }\n if (m.schemaVersion !== 1) {\n errors.push({ message: 'must be 1', path: 'schemaVersion' });\n }\n if (typeof m.name !== 'string' || !m.name) {\n errors.push({ message: 'must be a non-empty string', path: 'name' });\n }\n if (typeof m.owner !== 'string' || !m.owner) {\n errors.push({ message: 'must be a non-empty string', path: 'owner' });\n }\n if (m.visibility !== 'oss' && m.visibility !== 'private') {\n errors.push({ message: \"must be 'oss' or 'private'\", path: 'visibility' });\n }\n for (const key of ['hasSite', 'hasPackages', 'publishesToNpm'] as const) {\n if (typeof m[key] !== 'boolean') {\n errors.push({ message: 'must be a boolean', path: key });\n }\n }\n if (!Array.isArray(m.commitScopes)) {\n errors.push({ message: 'must be an array of strings', path: 'commitScopes' });\n }\n if (!m.contactEmails || typeof m.contactEmails !== 'object') {\n errors.push({ message: 'must be an object', path: 'contactEmails' });\n } else {\n for (const key of ['security', 'conduct'] as const) {\n if (typeof m.contactEmails[key] !== 'string' || !m.contactEmails[key]) {\n errors.push({\n message: 'must be a non-empty string',\n path: `contactEmails.${key}`,\n });\n }\n }\n }\n return errors;\n}\n\nexport function loadManifest(cwd: string): PrecisaManifest {\n const path = resolve(cwd, MANIFEST_FILENAME);\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(path, 'utf-8'));\n } catch (err) {\n throw new Error(`Failed to read ${MANIFEST_FILENAME} at ${path}: ${(err as Error).message}`);\n }\n const errors = validateManifest(raw);\n if (errors.length > 0) {\n const detail = errors.map((e) => ` - ${e.path}: ${e.message}`).join('\\n');\n throw new Error(`Invalid ${MANIFEST_FILENAME}:\\n${detail}`);\n }\n return raw as PrecisaManifest;\n}\n\nexport function writeManifest(cwd: string, manifest: PrecisaManifest): void {\n const path = resolve(cwd, MANIFEST_FILENAME);\n writeFileSync(path, `${JSON.stringify(manifest, null, 2)}\\n`);\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync, mkdirSync, readdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport prompts from 'prompts';\n\nimport { applyTemplate } from '../lib/merge.js';\nimport { loadTemplateManifest, readTemplateSource, renderTokens } from '../lib/templates.js';\nimport {\n DEFAULT_MANIFEST_FIELDS,\n isRequired,\n type PrecisaManifest,\n tokenContext,\n writeManifest,\n} from '../manifest.js';\n\nexport interface NewOptions {\n dryRun: boolean;\n profile: string;\n}\n\nconst PROFILES = {\n 'oss-library': {\n hasPackages: true,\n hasSite: false,\n publishesToNpm: true,\n visibility: 'oss',\n },\n 'oss-site': {\n hasPackages: false,\n hasSite: true,\n publishesToNpm: false,\n visibility: 'oss',\n },\n 'private-app': {\n hasPackages: true,\n hasSite: true,\n publishesToNpm: false,\n visibility: 'private',\n },\n} as const satisfies Record<\n string,\n Pick<PrecisaManifest, 'visibility' | 'hasSite' | 'hasPackages' | 'publishesToNpm'>\n>;\n\nexport async function runNew(repoName: string, opts: NewOptions): Promise<void> {\n const targetDir = resolve(process.cwd(), repoName);\n\n if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {\n console.error(chalk.red(`\\nError: ${targetDir} already exists and is not empty.`));\n process.exit(1);\n }\n\n const profile = PROFILES[opts.profile as keyof typeof PROFILES];\n if (!profile) {\n console.error(\n chalk.red(`\\nError: unknown profile '${opts.profile}'. Choose one of:`),\n Object.keys(PROFILES).join(', '),\n );\n process.exit(1);\n }\n\n console.log(chalk.bold.cyan(`\\nprecisa new ${repoName}`));\n console.log(chalk.dim(`profile: ${opts.profile}${opts.dryRun ? ' (dry-run)' : ''}`));\n console.log(chalk.dim(`target: ${targetDir}\\n`));\n\n const answers = await prompts(\n [\n {\n initial: 'Precisa-Saude',\n message: 'GitHub owner (org or user):',\n name: 'owner',\n type: 'text',\n },\n {\n initial: 'security@precisa-saude.com.br',\n message: 'Security contact email:',\n name: 'securityEmail',\n type: 'text',\n },\n {\n initial: 'conduct@precisa-saude.com.br',\n message: 'Code-of-conduct contact email:',\n name: 'conductEmail',\n type: 'text',\n },\n {\n initial: 'docs,ci,deps',\n message: 'Commit scopes (comma-separated):',\n name: 'commitScopes',\n separator: ',',\n type: 'list',\n },\n ],\n { onCancel: () => process.exit(1) },\n );\n\n const manifest: PrecisaManifest = {\n ...DEFAULT_MANIFEST_FIELDS,\n ...profile,\n commitScopes: answers.commitScopes,\n contactEmails: {\n conduct: answers.conductEmail,\n security: answers.securityEmail,\n },\n name: repoName,\n owner: answers.owner,\n };\n\n const spinner = ora('Rendering templates').start();\n try {\n if (!opts.dryRun) mkdirSync(targetDir, { recursive: true });\n\n const entries = loadTemplateManifest().filter((e) => isRequired(e.required_when, manifest));\n const context = tokenContext(manifest);\n\n let created = 0;\n let skipped = 0;\n\n for (const entry of entries) {\n const source = readTemplateSource(entry);\n const rendered = renderTokens(source, context);\n const outcome = applyTemplate({\n cwd: targetDir,\n dryRun: opts.dryRun,\n entry,\n rendered,\n });\n if (outcome.kind === 'create') created += 1;\n else if (outcome.kind === 'skip-exists') skipped += 1;\n else if (outcome.kind === 'error') {\n spinner.fail(`Failed on ${entry.target}`);\n console.error(chalk.red(outcome.message));\n process.exit(1);\n }\n }\n\n spinner.succeed(\n opts.dryRun\n ? `Would render ${created} file${created === 1 ? '' : 's'} (${skipped} skipped)`\n : `Rendered ${created} file${created === 1 ? '' : 's'} (${skipped} skipped)`,\n );\n\n if (opts.dryRun) return;\n\n writeManifest(targetDir, manifest);\n console.log(chalk.dim(` wrote .precisa.json`));\n\n const gitStep = ora('git init + pnpm install').start();\n try {\n execSync('git init --initial-branch=main', { cwd: targetDir, stdio: 'pipe' });\n execSync('pnpm install', { cwd: targetDir, stdio: 'pipe' });\n gitStep.succeed('git init + pnpm install complete');\n } catch (err) {\n gitStep.warn(\n `git/pnpm setup skipped: ${(err as Error).message}. Run manually in ${targetDir}.`,\n );\n }\n\n console.log(chalk.bold.green(`\\n✓ ${repoName} scaffolded at ${targetDir}`));\n console.log(chalk.dim('\\nNext:'));\n console.log(chalk.dim(` cd ${repoName}`));\n console.log(chalk.dim(` # start adding your code`));\n } catch (err) {\n spinner.fail((err as Error).message);\n process.exit(1);\n }\n}\n","import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\n\nimport type { MergeStrategy, TemplateEntry } from './templates.js';\n\nexport type ApplyOutcome =\n | { kind: 'create'; target: string; rendered: string }\n | { kind: 'update'; target: string; previous: string; rendered: string }\n | { kind: 'skip-identical'; target: string }\n | { kind: 'preserve'; target: string; current: string; rendered: string }\n | { kind: 'skip-exists'; target: string }\n | { kind: 'merge-json'; target: string; previous: string; rendered: string }\n | { kind: 'error'; target: string; message: string };\n\nexport interface ApplyOptions {\n /** Repo root to apply into. */\n cwd: string;\n /** When true, compute the outcome but do not write to disk. */\n dryRun: boolean;\n /** Template entry being applied. */\n entry: TemplateEntry;\n /** Rendered template content (post token substitution). */\n rendered: string;\n}\n\n/**\n * Apply a single template file to the target repo. Returns the outcome\n * so callers can print a per-file status row and, on dry-run, a diff.\n */\nexport function applyTemplate({ cwd, dryRun, entry, rendered }: ApplyOptions): ApplyOutcome {\n const targetPath = resolve(cwd, entry.target);\n const exists = existsSync(targetPath);\n\n if (!exists) {\n if (!dryRun) writeFile(targetPath, rendered, entry.executable === true);\n return { kind: 'create', rendered, target: entry.target };\n }\n\n const current = readFileSync(targetPath, 'utf-8');\n\n switch (entry.merge_strategy as MergeStrategy) {\n case 'overwrite': {\n if (current === rendered) {\n return { kind: 'skip-identical', target: entry.target };\n }\n if (!dryRun) writeFile(targetPath, rendered, entry.executable === true);\n return { kind: 'update', previous: current, rendered, target: entry.target };\n }\n case 'skip_if_exists': {\n return { kind: 'skip-exists', target: entry.target };\n }\n case 'preserve': {\n return { current, kind: 'preserve', rendered, target: entry.target };\n }\n case 'merge_json': {\n let merged: string;\n try {\n merged = mergeJson(current, rendered);\n } catch (err) {\n return {\n kind: 'error',\n message: `JSON merge failed: ${(err as Error).message}`,\n target: entry.target,\n };\n }\n if (current === merged) {\n return { kind: 'skip-identical', target: entry.target };\n }\n if (!dryRun) writeFile(targetPath, merged, entry.executable === true);\n return { kind: 'merge-json', previous: current, rendered: merged, target: entry.target };\n }\n default: {\n return {\n kind: 'error',\n message: `Unknown merge_strategy '${entry.merge_strategy}'`,\n target: entry.target,\n };\n }\n }\n}\n\nfunction writeFile(path: string, contents: string, executable: boolean): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, contents);\n if (executable) chmodSync(path, 0o755);\n}\n\n/**\n * Shallow 3-way-ish JSON merge — the current file wins on conflicts so\n * repo-local customizations aren't clobbered. Objects are merged\n * key-by-key; arrays and scalars are replaced entirely by the current\n * value when present (falls back to the template value).\n *\n * If parsing fails, throws so the caller can report the error.\n */\nexport function mergeJson(currentRaw: string, templateRaw: string): string {\n const current = JSON.parse(currentRaw) as unknown;\n const template = JSON.parse(templateRaw) as unknown;\n const merged = mergeValue(template, current);\n return `${JSON.stringify(merged, null, 2)}\\n`;\n}\n\nfunction mergeValue(template: unknown, current: unknown): unknown {\n if (current === undefined) return template;\n if (\n typeof template !== 'object' ||\n template === null ||\n Array.isArray(template) ||\n typeof current !== 'object' ||\n current === null ||\n Array.isArray(current)\n ) {\n // Scalars / arrays: current value wins (never clobber repo-local values).\n return current;\n }\n const out: Record<string, unknown> = {};\n const keys = new Set([\n ...Object.keys(template as Record<string, unknown>),\n ...Object.keys(current as Record<string, unknown>),\n ]);\n for (const k of keys) {\n out[k] = mergeValue(\n (template as Record<string, unknown>)[k],\n (current as Record<string, unknown>)[k],\n );\n }\n return out;\n}\n","import chalk from 'chalk';\n\nimport { colorDiff } from '../lib/diff.js';\nimport { type ApplyOutcome, applyTemplate } from '../lib/merge.js';\nimport { loadTemplateManifest, readTemplateSource, renderTokens } from '../lib/templates.js';\nimport { isRequired, loadManifest, tokenContext } from '../manifest.js';\n\nexport interface SyncOptions {\n dryRun: boolean;\n}\n\nexport async function runSync(opts: SyncOptions): Promise<void> {\n const cwd = process.cwd();\n console.log(chalk.bold.cyan(`\\nprecisa sync${opts.dryRun ? ' --dry-run' : ''}`));\n\n let manifest;\n try {\n manifest = loadManifest(cwd);\n } catch (err) {\n console.error(chalk.red(`\\n${(err as Error).message}`));\n console.error(\n chalk.dim('\\n Run `precisa new <repo>` in an empty directory to scaffold a new repo,'),\n );\n console.error(chalk.dim(' or create a `.precisa.json` manifest manually.'));\n process.exit(1);\n }\n\n const entries = loadTemplateManifest().filter((e) => isRequired(e.required_when, manifest));\n const context = tokenContext(manifest);\n\n const outcomes: ApplyOutcome[] = [];\n for (const entry of entries) {\n const source = readTemplateSource(entry);\n const rendered = renderTokens(source, context);\n outcomes.push(\n applyTemplate({\n cwd,\n dryRun: opts.dryRun,\n entry,\n rendered,\n }),\n );\n }\n\n console.log('');\n for (const o of outcomes) {\n printOutcome(o, opts.dryRun);\n }\n\n const summary = summarize(outcomes);\n console.log('');\n console.log(\n chalk.dim(\n `${summary.create} create, ${summary.update} update, ${summary.mergeJson} merge-json, ${summary.preserve} preserve, ${summary.skip} skip, ${summary.error} error`,\n ),\n );\n\n if (summary.error > 0) process.exit(1);\n}\n\ninterface Summary {\n create: number;\n error: number;\n mergeJson: number;\n preserve: number;\n skip: number;\n update: number;\n}\n\nfunction summarize(outcomes: ApplyOutcome[]): Summary {\n const s: Summary = { create: 0, error: 0, mergeJson: 0, preserve: 0, skip: 0, update: 0 };\n for (const o of outcomes) {\n switch (o.kind) {\n case 'create':\n s.create += 1;\n break;\n case 'update':\n s.update += 1;\n break;\n case 'merge-json':\n s.mergeJson += 1;\n break;\n case 'preserve':\n s.preserve += 1;\n break;\n case 'skip-identical':\n case 'skip-exists':\n s.skip += 1;\n break;\n case 'error':\n s.error += 1;\n break;\n }\n }\n return s;\n}\n\nfunction printOutcome(o: ApplyOutcome, dryRun: boolean): void {\n const prefix = dryRun ? '(dry-run) ' : '';\n switch (o.kind) {\n case 'create':\n console.log(`${chalk.green('+')} ${prefix}create ${o.target}`);\n return;\n case 'update':\n console.log(`${chalk.yellow('~')} ${prefix}update ${o.target}`);\n if (dryRun) console.log(indent(colorDiff(o.target, o.previous, o.rendered)));\n return;\n case 'merge-json':\n console.log(`${chalk.yellow('~')} ${prefix}merge ${o.target}`);\n if (dryRun) console.log(indent(colorDiff(o.target, o.previous, o.rendered)));\n return;\n case 'preserve':\n console.log(\n `${chalk.blue('i')} ${prefix}preserve ${o.target} (template differs; not written)`,\n );\n if (dryRun && o.current !== o.rendered) {\n console.log(indent(colorDiff(o.target, o.current, o.rendered)));\n }\n return;\n case 'skip-identical':\n console.log(`${chalk.dim('=')} ${prefix}unchanged ${o.target}`);\n return;\n case 'skip-exists':\n console.log(`${chalk.dim('s')} ${prefix}skip ${o.target} (already exists)`);\n return;\n case 'error':\n console.log(`${chalk.red('!')} ${prefix}error ${o.target}: ${o.message}`);\n return;\n }\n}\n\nfunction indent(text: string): string {\n return text\n .split('\\n')\n .map((l) => ` ${l}`)\n .join('\\n');\n}\n","import chalk from 'chalk';\nimport { createPatch } from 'diff';\n\n/**\n * Format a unified diff with ANSI colors for terminal display.\n * Skips the `@@ hunk @@` metadata lines for a cleaner look.\n */\nexport function colorDiff(filename: string, previous: string, next: string): string {\n const patch = createPatch(filename, previous, next, '', '');\n const lines = patch.split('\\n');\n const out: string[] = [];\n for (const line of lines) {\n if (line.startsWith('---') || line.startsWith('+++')) continue;\n if (line.startsWith('@@')) {\n out.push(chalk.cyan(line));\n continue;\n }\n if (line.startsWith('+')) {\n out.push(chalk.green(line));\n continue;\n }\n if (line.startsWith('-')) {\n out.push(chalk.red(line));\n continue;\n }\n out.push(line);\n }\n return out.join('\\n');\n}\n"],"mappings":"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","names":["existsSync","readFileSync","resolve","resolve","resolve","readFileSync","resolve","resolve","existsSync","readFileSync","existsSync","mkdirSync","resolve","chalk","existsSync","readFileSync","writeFileSync","dirname","resolve","resolve","existsSync","chalk","mkdirSync","chalk","chalk","chalk"]}
1
+ {"version":3,"sources":["../src/bin.ts","../src/commands/doctor.ts","../src/lib/templates.ts","../src/lib/paths.ts","../src/manifest.ts","../src/commands/new.ts","../src/lib/merge.ts","../src/commands/sync.ts","../src/lib/diff.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\n\nimport { runDoctor } from './commands/doctor.js';\nimport { runNew } from './commands/new.js';\nimport { runSync } from './commands/sync.js';\n\nconst program = new Command();\n\nprogram\n .name('precisa')\n .description('Bootstrap and sync Precisa Saúde repositories.')\n .version('0.0.0');\n\nprogram\n .command('new <repo-name>')\n .description('Scaffold a new repository from the shared templates.')\n .option('--profile <profile>', 'preset: oss-library | oss-site | private-app', 'oss-library')\n .option('--dry-run', 'print what would be written without touching disk', false)\n .option('--non-interactive', 'skip prompts; use --owner/--scopes/etc. flags instead', false)\n .option('--owner <org>', 'GitHub owner (org or user)', 'Precisa-Saude')\n .option(\n '--scopes <csv>',\n 'Comma-separated commit scopes (e.g. \"core,docs,ci,deps\")',\n 'docs,ci,deps',\n )\n .option('--security-email <email>', 'Security contact email', 'security@precisa-saude.com.br')\n .option(\n '--conduct-email <email>',\n 'Code-of-conduct contact email',\n 'conduct@precisa-saude.com.br',\n )\n .action(\n async (\n repoName: string,\n opts: {\n profile: string;\n dryRun: boolean;\n nonInteractive: boolean;\n owner: string;\n scopes: string;\n securityEmail: string;\n conductEmail: string;\n },\n ) => {\n await runNew(repoName, opts);\n },\n );\n\nprogram\n .command('sync')\n .description(\"Re-render templates against this repo's .precisa.json manifest.\")\n .option('--dry-run', 'print a diff without writing', false)\n .action(async (opts: { dryRun: boolean }) => {\n await runSync(opts);\n });\n\nprogram\n .command('doctor')\n .description('Audit this repo against the templates and report drift.')\n .action(async () => {\n await runDoctor();\n });\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n console.error(err);\n process.exit(1);\n});\n","import { existsSync, readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport chalk from 'chalk';\n\nimport {\n loadTemplateManifest,\n readTemplateSource,\n renderTokens,\n type TemplateEntry,\n} from '../lib/templates.js';\nimport { isRequired, loadManifest, tokenContext } from '../manifest.js';\n\ntype Severity = 'ok' | 'info' | 'warning' | 'error';\n\ninterface DriftReport {\n message: string;\n severity: Severity;\n target: string;\n}\n\nexport async function runDoctor(): Promise<void> {\n const cwd = process.cwd();\n console.log(chalk.bold.cyan('\\nprecisa doctor'));\n\n let manifest;\n try {\n manifest = loadManifest(cwd);\n } catch (err) {\n console.error(chalk.red(`\\n${(err as Error).message}`));\n process.exit(1);\n }\n\n const entries = loadTemplateManifest();\n const context = tokenContext(manifest);\n\n const reports: DriftReport[] = [];\n\n for (const entry of entries) {\n const required = isRequired(entry.required_when, manifest);\n const targetPath = resolve(cwd, entry.target);\n const exists = existsSync(targetPath);\n\n if (!required && !exists) continue;\n if (!required && exists) {\n reports.push({\n message: `Present but not required by the manifest profile (ok)`,\n severity: 'info',\n target: entry.target,\n });\n continue;\n }\n if (!exists) {\n reports.push({\n message: 'Missing',\n severity: 'error',\n target: entry.target,\n });\n continue;\n }\n\n // Exists + required — compare to rendered template\n const source = readTemplateSource(entry);\n const rendered = renderTokens(source, context);\n const current = readFileSync(targetPath, 'utf-8');\n reports.push(compareContent(entry, current, rendered));\n }\n\n console.log('');\n let errors = 0;\n let warnings = 0;\n let infos = 0;\n let oks = 0;\n\n for (const r of reports) {\n const icon = iconFor(r.severity);\n const line = `${icon} ${r.target}${r.message ? chalk.dim(` — ${r.message}`) : ''}`;\n console.log(line);\n if (r.severity === 'error') errors += 1;\n else if (r.severity === 'warning') warnings += 1;\n else if (r.severity === 'info') infos += 1;\n else oks += 1;\n }\n\n console.log('');\n console.log(chalk.dim(`${oks} ok, ${warnings} warning, ${errors} error, ${infos} info`));\n\n if (errors > 0) process.exit(1);\n}\n\nfunction compareContent(entry: TemplateEntry, current: string, rendered: string): DriftReport {\n if (current === rendered) {\n return { message: '', severity: 'ok', target: entry.target };\n }\n\n // Non-overwrite strategies don't re-render on sync — treat drift as\n // informational, not a warning. Makes the doctor output scannable:\n // warnings are the files sync WOULD change; infos are files where\n // drift is intentional (customized scaffold, preserved docs).\n if (entry.merge_strategy === 'preserve') {\n return {\n message: 'Differs from template (preserve strategy — suggestion only)',\n severity: 'info',\n target: entry.target,\n };\n }\n if (entry.merge_strategy === 'skip_if_exists') {\n return {\n message: 'Differs from template (scaffold-only; sync will not overwrite)',\n severity: 'info',\n target: entry.target,\n };\n }\n\n return {\n message: 'Differs from template (run `precisa sync` to update)',\n severity: 'warning',\n target: entry.target,\n };\n}\n\nfunction iconFor(severity: Severity): string {\n switch (severity) {\n case 'ok':\n return chalk.green('✓');\n case 'info':\n return chalk.blue('i');\n case 'warning':\n return chalk.yellow('!');\n case 'error':\n return chalk.red('✗');\n }\n}\n","import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { parse as parseYaml } from 'yaml';\n\nimport type { RequiredWhen } from '../manifest.js';\nimport { resolveTemplatesDir } from './paths.js';\n\nexport type MergeStrategy = 'overwrite' | 'merge_json' | 'preserve' | 'skip_if_exists';\n\nexport interface TemplateEntry {\n /** Whether to mark the rendered file executable (chmod +x). */\n executable?: boolean;\n /** How to merge when the target already exists. */\n merge_strategy: MergeStrategy;\n /** Gate for inclusion. */\n required_when: RequiredWhen;\n /** Path relative to templates/. */\n source: string;\n /** Path in the target repo (relative to the repo root). */\n target: string;\n}\n\ninterface TemplateManifestFile {\n templates: TemplateEntry[];\n}\n\n/**\n * Load the `templates.manifest.yml` from the bundled templates directory.\n * Throws if the file is missing or the shape is wrong.\n */\nexport function loadTemplateManifest(): TemplateEntry[] {\n const dir = resolveTemplatesDir();\n const manifestPath = resolve(dir, 'templates.manifest.yml');\n const raw = readFileSync(manifestPath, 'utf-8');\n const parsed = parseYaml(raw) as TemplateManifestFile | undefined;\n if (!parsed || !Array.isArray(parsed.templates)) {\n throw new Error(`${manifestPath}: expected { templates: [...] } at root`);\n }\n for (const [i, t] of parsed.templates.entries()) {\n if (!t.source || !t.target) {\n throw new Error(`${manifestPath}: entry ${i} is missing source or target`);\n }\n }\n return parsed.templates;\n}\n\nexport function readTemplateSource(entry: TemplateEntry): string {\n const dir = resolveTemplatesDir();\n const path = resolve(dir, entry.source);\n return readFileSync(path, 'utf-8');\n}\n\n/**\n * Substitute `{{TOKEN}}` placeholders with values from `context`. Unknown\n * tokens are left untouched (keeps the double braces visible so bad keys\n * are easy to spot in rendered output).\n */\nexport function renderTokens(source: string, context: Record<string, string>): string {\n return source.replace(/\\{\\{([A-Z_]+)\\}\\}/g, (match, token: string) => {\n return Object.prototype.hasOwnProperty.call(context, token) ? context[token]! : match;\n });\n}\n","import { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Locate the bundled `templates/` directory.\n *\n * When published to npm, the templates live at `dist/templates/` inside\n * the CLI package (copied by `scripts/copy-templates.mjs` during build).\n *\n * When running from the monorepo (e.g. `pnpm --filter @precisa-saude/cli dev`),\n * the templates live at the repo root — fall back to walking up from the\n * package directory.\n */\nexport function resolveTemplatesDir(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n\n // Published case: cli package's dist/templates\n const bundled = resolve(here, 'templates');\n if (existsSync(bundled)) return bundled;\n\n // Dev case: walk up from packages/cli/{src|dist} to the monorepo root\n const repoRoot = resolve(here, '..', '..', '..', '..');\n const repoTemplates = resolve(repoRoot, 'templates');\n if (existsSync(repoTemplates)) return repoTemplates;\n\n // Sibling-in-package case (tsup output): packages/cli/dist -> ../../templates\n const packageParent = resolve(here, '..', '..', 'templates');\n if (existsSync(packageParent)) return packageParent;\n\n throw new Error(\n `Could not locate templates/ directory. Searched:\\n ${bundled}\\n ${repoTemplates}\\n ${packageParent}`,\n );\n}\n","import { readFileSync, writeFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\n/**\n * `.precisa.json` manifest — sits at the root of every consumer repo and\n * declares the repo's profile so `precisa sync` and `precisa doctor` know\n * which templates to render and which rules to enforce.\n */\nexport interface PrecisaManifest {\n /** Commitlint scope enum for this repo. */\n commitScopes: string[];\n\n /** Contact emails used in governance doc templates. */\n contactEmails: {\n security: string;\n conduct: string;\n };\n\n /** Does this repo have publishable packages/*? */\n hasPackages: boolean;\n\n /** Does this repo ship a website? Controls preview-deploy workflows. */\n hasSite: boolean;\n\n /** Repository name (typically matches the GitHub repo name). */\n name: string;\n\n /** Pinned runtime versions. Default: node=22, pnpm=9.15.9. */\n nodeVersion?: string;\n\n /** GitHub organization or user that owns the repo (e.g. `Precisa-Saude`). */\n owner: string;\n\n pnpmVersion?: string;\n\n /** Publishes any workspace package to npm? */\n publishesToNpm: boolean;\n\n /** Schema version of this manifest file. Bump when the schema changes. */\n schemaVersion: 1;\n /** Public-OSS or private-internal. Controls which templates are rendered. */\n visibility: 'oss' | 'private';\n}\n\nexport const MANIFEST_FILENAME = '.precisa.json';\n\nexport const DEFAULT_MANIFEST_FIELDS = {\n hasPackages: true,\n hasSite: false,\n nodeVersion: '22',\n pnpmVersion: '9.15.9',\n publishesToNpm: true,\n schemaVersion: 1 as const,\n visibility: 'oss' as const,\n};\n\n/** Template gate values — `required_when` in templates.manifest.yml. */\nexport type RequiredWhen =\n | 'always'\n | 'never'\n | 'oss'\n | 'private'\n | 'has_site'\n | 'has_packages'\n | 'publishes_to_npm';\n\n/** Returns true when a template's `required_when` gate applies to this manifest. */\nexport function isRequired(when: RequiredWhen, manifest: PrecisaManifest): boolean {\n switch (when) {\n case 'always':\n return true;\n case 'never':\n return false;\n case 'oss':\n return manifest.visibility === 'oss';\n case 'private':\n return manifest.visibility === 'private';\n case 'has_site':\n return manifest.hasSite;\n case 'has_packages':\n return manifest.hasPackages;\n case 'publishes_to_npm':\n return manifest.publishesToNpm;\n default:\n return true;\n }\n}\n\n/**\n * Build the token substitution map from a manifest. Keys are `{{TOKEN}}`\n * (without braces); values are always strings.\n */\nexport function tokenContext(manifest: PrecisaManifest): Record<string, string> {\n return {\n COMMIT_SCOPES: manifest.commitScopes.join(','),\n // For human-readable docs (AGENTS.md, README) — backticked list.\n COMMIT_SCOPES_HUMAN: manifest.commitScopes.map((s) => `\\`${s}\\``).join(', '),\n // For `.commitlintrc.cjs` — scope array spelled as JS string literals,\n // ready to drop inside `[ ... ]`.\n COMMIT_SCOPES_JSON: manifest.commitScopes.map((s) => `'${s}'`).join(', '),\n CONDUCT_EMAIL: manifest.contactEmails.conduct,\n HAS_PACKAGES: String(manifest.hasPackages),\n HAS_SITE: String(manifest.hasSite),\n NODE_VERSION: manifest.nodeVersion ?? '22',\n OWNER_ORG: manifest.owner,\n PNPM_VERSION: manifest.pnpmVersion ?? '9.15.9',\n PUBLISHES_TO_NPM: String(manifest.publishesToNpm),\n REPO_NAME: manifest.name,\n REPO_SLUG: `${manifest.owner}/${manifest.name}`,\n SECURITY_EMAIL: manifest.contactEmails.security,\n VISIBILITY: manifest.visibility,\n };\n}\n\nexport interface ManifestValidationError {\n message: string;\n path: string;\n}\n\nexport function validateManifest(raw: unknown): ManifestValidationError[] {\n const errors: ManifestValidationError[] = [];\n const m = raw as Partial<PrecisaManifest> | undefined;\n if (!m || typeof m !== 'object') {\n return [{ message: 'manifest must be a JSON object', path: '$' }];\n }\n if (m.schemaVersion !== 1) {\n errors.push({ message: 'must be 1', path: 'schemaVersion' });\n }\n if (typeof m.name !== 'string' || !m.name) {\n errors.push({ message: 'must be a non-empty string', path: 'name' });\n }\n if (typeof m.owner !== 'string' || !m.owner) {\n errors.push({ message: 'must be a non-empty string', path: 'owner' });\n }\n if (m.visibility !== 'oss' && m.visibility !== 'private') {\n errors.push({ message: \"must be 'oss' or 'private'\", path: 'visibility' });\n }\n for (const key of ['hasSite', 'hasPackages', 'publishesToNpm'] as const) {\n if (typeof m[key] !== 'boolean') {\n errors.push({ message: 'must be a boolean', path: key });\n }\n }\n if (!Array.isArray(m.commitScopes)) {\n errors.push({ message: 'must be an array of strings', path: 'commitScopes' });\n }\n if (!m.contactEmails || typeof m.contactEmails !== 'object') {\n errors.push({ message: 'must be an object', path: 'contactEmails' });\n } else {\n for (const key of ['security', 'conduct'] as const) {\n if (typeof m.contactEmails[key] !== 'string' || !m.contactEmails[key]) {\n errors.push({\n message: 'must be a non-empty string',\n path: `contactEmails.${key}`,\n });\n }\n }\n }\n return errors;\n}\n\nexport function loadManifest(cwd: string): PrecisaManifest {\n const path = resolve(cwd, MANIFEST_FILENAME);\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(path, 'utf-8'));\n } catch (err) {\n throw new Error(`Failed to read ${MANIFEST_FILENAME} at ${path}: ${(err as Error).message}`);\n }\n const errors = validateManifest(raw);\n if (errors.length > 0) {\n const detail = errors.map((e) => ` - ${e.path}: ${e.message}`).join('\\n');\n throw new Error(`Invalid ${MANIFEST_FILENAME}:\\n${detail}`);\n }\n return raw as PrecisaManifest;\n}\n\nexport function writeManifest(cwd: string, manifest: PrecisaManifest): void {\n const path = resolve(cwd, MANIFEST_FILENAME);\n writeFileSync(path, `${JSON.stringify(manifest, null, 2)}\\n`);\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync, mkdirSync, readdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport prompts from 'prompts';\n\nimport { applyTemplate } from '../lib/merge.js';\nimport { loadTemplateManifest, readTemplateSource, renderTokens } from '../lib/templates.js';\nimport {\n DEFAULT_MANIFEST_FIELDS,\n isRequired,\n type PrecisaManifest,\n tokenContext,\n writeManifest,\n} from '../manifest.js';\n\nexport interface NewOptions {\n conductEmail?: string;\n dryRun: boolean;\n /** Skip interactive prompts; require --owner/--scopes/--*-email flags. */\n nonInteractive?: boolean;\n owner?: string;\n profile: string;\n /** Comma-separated list, e.g. \"core,docs,ci,deps\". */\n scopes?: string;\n securityEmail?: string;\n}\n\nconst PROFILES = {\n 'oss-library': {\n hasPackages: true,\n hasSite: false,\n publishesToNpm: true,\n visibility: 'oss',\n },\n 'oss-site': {\n hasPackages: false,\n hasSite: true,\n publishesToNpm: false,\n visibility: 'oss',\n },\n 'private-app': {\n hasPackages: true,\n hasSite: true,\n publishesToNpm: false,\n visibility: 'private',\n },\n} as const satisfies Record<\n string,\n Pick<PrecisaManifest, 'visibility' | 'hasSite' | 'hasPackages' | 'publishesToNpm'>\n>;\n\nexport async function runNew(repoName: string, opts: NewOptions): Promise<void> {\n const targetDir = resolve(process.cwd(), repoName);\n\n if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {\n console.error(chalk.red(`\\nError: ${targetDir} already exists and is not empty.`));\n process.exit(1);\n }\n\n const profile = PROFILES[opts.profile as keyof typeof PROFILES];\n if (!profile) {\n console.error(\n chalk.red(`\\nError: unknown profile '${opts.profile}'. Choose one of:`),\n Object.keys(PROFILES).join(', '),\n );\n process.exit(1);\n }\n\n console.log(chalk.bold.cyan(`\\nprecisa new ${repoName}`));\n console.log(chalk.dim(`profile: ${opts.profile}${opts.dryRun ? ' (dry-run)' : ''}`));\n console.log(chalk.dim(`target: ${targetDir}\\n`));\n\n const answers = opts.nonInteractive\n ? {\n commitScopes: (opts.scopes ?? 'docs,ci,deps')\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n conductEmail: opts.conductEmail ?? 'conduct@precisa-saude.com.br',\n owner: opts.owner ?? 'Precisa-Saude',\n securityEmail: opts.securityEmail ?? 'security@precisa-saude.com.br',\n }\n : await prompts(\n [\n {\n initial: opts.owner ?? 'Precisa-Saude',\n message: 'GitHub owner (org or user):',\n name: 'owner',\n type: 'text',\n },\n {\n initial: opts.securityEmail ?? 'security@precisa-saude.com.br',\n message: 'Security contact email:',\n name: 'securityEmail',\n type: 'text',\n },\n {\n initial: opts.conductEmail ?? 'conduct@precisa-saude.com.br',\n message: 'Code-of-conduct contact email:',\n name: 'conductEmail',\n type: 'text',\n },\n {\n initial: opts.scopes ?? 'docs,ci,deps',\n message: 'Commit scopes (comma-separated):',\n name: 'commitScopes',\n separator: ',',\n type: 'list',\n },\n ],\n { onCancel: () => process.exit(1) },\n );\n\n const manifest: PrecisaManifest = {\n ...DEFAULT_MANIFEST_FIELDS,\n ...profile,\n commitScopes: answers.commitScopes,\n contactEmails: {\n conduct: answers.conductEmail,\n security: answers.securityEmail,\n },\n name: repoName,\n owner: answers.owner,\n };\n\n const spinner = ora('Rendering templates').start();\n try {\n if (!opts.dryRun) mkdirSync(targetDir, { recursive: true });\n\n const entries = loadTemplateManifest().filter((e) => isRequired(e.required_when, manifest));\n const context = tokenContext(manifest);\n\n let created = 0;\n let skipped = 0;\n\n for (const entry of entries) {\n const source = readTemplateSource(entry);\n const rendered = renderTokens(source, context);\n const outcome = applyTemplate({\n cwd: targetDir,\n dryRun: opts.dryRun,\n entry,\n rendered,\n });\n if (outcome.kind === 'create') created += 1;\n else if (outcome.kind === 'skip-exists') skipped += 1;\n else if (outcome.kind === 'error') {\n spinner.fail(`Failed on ${entry.target}`);\n console.error(chalk.red(outcome.message));\n process.exit(1);\n }\n }\n\n spinner.succeed(\n opts.dryRun\n ? `Would render ${created} file${created === 1 ? '' : 's'} (${skipped} skipped)`\n : `Rendered ${created} file${created === 1 ? '' : 's'} (${skipped} skipped)`,\n );\n\n if (opts.dryRun) return;\n\n writeManifest(targetDir, manifest);\n console.log(chalk.dim(` wrote .precisa.json`));\n\n const gitStep = ora('git init + pnpm install').start();\n try {\n execSync('git init --initial-branch=main', { cwd: targetDir, stdio: 'pipe' });\n execSync('pnpm install', { cwd: targetDir, stdio: 'pipe' });\n gitStep.succeed('git init + pnpm install complete');\n } catch (err) {\n gitStep.warn(\n `git/pnpm setup skipped: ${(err as Error).message}. Run manually in ${targetDir}.`,\n );\n }\n\n console.log(chalk.bold.green(`\\n✓ ${repoName} scaffolded at ${targetDir}`));\n console.log(chalk.dim('\\nNext:'));\n console.log(chalk.dim(` cd ${repoName}`));\n console.log(chalk.dim(` # start adding your code`));\n } catch (err) {\n spinner.fail((err as Error).message);\n process.exit(1);\n }\n}\n","import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\n\nimport type { MergeStrategy, TemplateEntry } from './templates.js';\n\nexport type ApplyOutcome =\n | { kind: 'create'; target: string; rendered: string }\n | { kind: 'update'; target: string; previous: string; rendered: string }\n | { kind: 'skip-identical'; target: string }\n | { kind: 'preserve'; target: string; current: string; rendered: string }\n | { kind: 'skip-exists'; target: string }\n | { kind: 'merge-json'; target: string; previous: string; rendered: string }\n | { kind: 'error'; target: string; message: string };\n\nexport interface ApplyOptions {\n /** Repo root to apply into. */\n cwd: string;\n /** When true, compute the outcome but do not write to disk. */\n dryRun: boolean;\n /** Template entry being applied. */\n entry: TemplateEntry;\n /** Rendered template content (post token substitution). */\n rendered: string;\n}\n\n/**\n * Apply a single template file to the target repo. Returns the outcome\n * so callers can print a per-file status row and, on dry-run, a diff.\n */\nexport function applyTemplate({ cwd, dryRun, entry, rendered }: ApplyOptions): ApplyOutcome {\n const targetPath = resolve(cwd, entry.target);\n const exists = existsSync(targetPath);\n\n if (!exists) {\n if (!dryRun) writeFile(targetPath, rendered, entry.executable === true);\n return { kind: 'create', rendered, target: entry.target };\n }\n\n const current = readFileSync(targetPath, 'utf-8');\n\n switch (entry.merge_strategy as MergeStrategy) {\n case 'overwrite': {\n if (current === rendered) {\n return { kind: 'skip-identical', target: entry.target };\n }\n if (!dryRun) writeFile(targetPath, rendered, entry.executable === true);\n return { kind: 'update', previous: current, rendered, target: entry.target };\n }\n case 'skip_if_exists': {\n return { kind: 'skip-exists', target: entry.target };\n }\n case 'preserve': {\n return { current, kind: 'preserve', rendered, target: entry.target };\n }\n case 'merge_json': {\n let merged: string;\n try {\n merged = mergeJson(current, rendered);\n } catch (err) {\n return {\n kind: 'error',\n message: `JSON merge failed: ${(err as Error).message}`,\n target: entry.target,\n };\n }\n if (current === merged) {\n return { kind: 'skip-identical', target: entry.target };\n }\n if (!dryRun) writeFile(targetPath, merged, entry.executable === true);\n return { kind: 'merge-json', previous: current, rendered: merged, target: entry.target };\n }\n default: {\n return {\n kind: 'error',\n message: `Unknown merge_strategy '${entry.merge_strategy}'`,\n target: entry.target,\n };\n }\n }\n}\n\nfunction writeFile(path: string, contents: string, executable: boolean): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, contents);\n if (executable) chmodSync(path, 0o755);\n}\n\n/**\n * Shallow 3-way-ish JSON merge — the current file wins on conflicts so\n * repo-local customizations aren't clobbered. Objects are merged\n * key-by-key; arrays and scalars are replaced entirely by the current\n * value when present (falls back to the template value).\n *\n * If parsing fails, throws so the caller can report the error.\n */\nexport function mergeJson(currentRaw: string, templateRaw: string): string {\n const current = JSON.parse(currentRaw) as unknown;\n const template = JSON.parse(templateRaw) as unknown;\n const merged = mergeValue(template, current);\n return `${JSON.stringify(merged, null, 2)}\\n`;\n}\n\nfunction mergeValue(template: unknown, current: unknown): unknown {\n if (current === undefined) return template;\n if (\n typeof template !== 'object' ||\n template === null ||\n Array.isArray(template) ||\n typeof current !== 'object' ||\n current === null ||\n Array.isArray(current)\n ) {\n // Scalars / arrays: current value wins (never clobber repo-local values).\n return current;\n }\n const out: Record<string, unknown> = {};\n const keys = new Set([\n ...Object.keys(template as Record<string, unknown>),\n ...Object.keys(current as Record<string, unknown>),\n ]);\n for (const k of keys) {\n out[k] = mergeValue(\n (template as Record<string, unknown>)[k],\n (current as Record<string, unknown>)[k],\n );\n }\n return out;\n}\n","import chalk from 'chalk';\n\nimport { colorDiff } from '../lib/diff.js';\nimport { type ApplyOutcome, applyTemplate } from '../lib/merge.js';\nimport { loadTemplateManifest, readTemplateSource, renderTokens } from '../lib/templates.js';\nimport { isRequired, loadManifest, tokenContext } from '../manifest.js';\n\nexport interface SyncOptions {\n dryRun: boolean;\n}\n\nexport async function runSync(opts: SyncOptions): Promise<void> {\n const cwd = process.cwd();\n console.log(chalk.bold.cyan(`\\nprecisa sync${opts.dryRun ? ' --dry-run' : ''}`));\n\n let manifest;\n try {\n manifest = loadManifest(cwd);\n } catch (err) {\n console.error(chalk.red(`\\n${(err as Error).message}`));\n console.error(\n chalk.dim('\\n Run `precisa new <repo>` in an empty directory to scaffold a new repo,'),\n );\n console.error(chalk.dim(' or create a `.precisa.json` manifest manually.'));\n process.exit(1);\n }\n\n const entries = loadTemplateManifest().filter((e) => isRequired(e.required_when, manifest));\n const context = tokenContext(manifest);\n\n const outcomes: ApplyOutcome[] = [];\n for (const entry of entries) {\n const source = readTemplateSource(entry);\n const rendered = renderTokens(source, context);\n outcomes.push(\n applyTemplate({\n cwd,\n dryRun: opts.dryRun,\n entry,\n rendered,\n }),\n );\n }\n\n console.log('');\n for (const o of outcomes) {\n printOutcome(o, opts.dryRun);\n }\n\n const summary = summarize(outcomes);\n console.log('');\n console.log(\n chalk.dim(\n `${summary.create} create, ${summary.update} update, ${summary.mergeJson} merge-json, ${summary.preserve} preserve, ${summary.skip} skip, ${summary.error} error`,\n ),\n );\n\n if (summary.error > 0) process.exit(1);\n}\n\ninterface Summary {\n create: number;\n error: number;\n mergeJson: number;\n preserve: number;\n skip: number;\n update: number;\n}\n\nfunction summarize(outcomes: ApplyOutcome[]): Summary {\n const s: Summary = { create: 0, error: 0, mergeJson: 0, preserve: 0, skip: 0, update: 0 };\n for (const o of outcomes) {\n switch (o.kind) {\n case 'create':\n s.create += 1;\n break;\n case 'update':\n s.update += 1;\n break;\n case 'merge-json':\n s.mergeJson += 1;\n break;\n case 'preserve':\n s.preserve += 1;\n break;\n case 'skip-identical':\n case 'skip-exists':\n s.skip += 1;\n break;\n case 'error':\n s.error += 1;\n break;\n }\n }\n return s;\n}\n\nfunction printOutcome(o: ApplyOutcome, dryRun: boolean): void {\n const prefix = dryRun ? '(dry-run) ' : '';\n switch (o.kind) {\n case 'create':\n console.log(`${chalk.green('+')} ${prefix}create ${o.target}`);\n return;\n case 'update':\n console.log(`${chalk.yellow('~')} ${prefix}update ${o.target}`);\n if (dryRun) console.log(indent(colorDiff(o.target, o.previous, o.rendered)));\n return;\n case 'merge-json':\n console.log(`${chalk.yellow('~')} ${prefix}merge ${o.target}`);\n if (dryRun) console.log(indent(colorDiff(o.target, o.previous, o.rendered)));\n return;\n case 'preserve':\n console.log(\n `${chalk.blue('i')} ${prefix}preserve ${o.target} (template differs; not written)`,\n );\n if (dryRun && o.current !== o.rendered) {\n console.log(indent(colorDiff(o.target, o.current, o.rendered)));\n }\n return;\n case 'skip-identical':\n console.log(`${chalk.dim('=')} ${prefix}unchanged ${o.target}`);\n return;\n case 'skip-exists':\n console.log(`${chalk.dim('s')} ${prefix}skip ${o.target} (already exists)`);\n return;\n case 'error':\n console.log(`${chalk.red('!')} ${prefix}error ${o.target}: ${o.message}`);\n return;\n }\n}\n\nfunction indent(text: string): string {\n return text\n .split('\\n')\n .map((l) => ` ${l}`)\n .join('\\n');\n}\n","import chalk from 'chalk';\nimport { createPatch } from 'diff';\n\n/**\n * Format a unified diff with ANSI colors for terminal display.\n * Skips the `@@ hunk @@` metadata lines for a cleaner look.\n */\nexport function colorDiff(filename: string, previous: string, next: string): string {\n const patch = createPatch(filename, previous, next, '', '');\n const lines = patch.split('\\n');\n const out: string[] = [];\n for (const line of lines) {\n if (line.startsWith('---') || line.startsWith('+++')) continue;\n if (line.startsWith('@@')) {\n out.push(chalk.cyan(line));\n continue;\n }\n if (line.startsWith('+')) {\n out.push(chalk.green(line));\n continue;\n }\n if (line.startsWith('-')) {\n out.push(chalk.red(line));\n continue;\n }\n out.push(line);\n }\n return out.join('\\n');\n}\n"],"mappings":"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","names":["existsSync","readFileSync","resolve","resolve","resolve","readFileSync","resolve","resolve","existsSync","readFileSync","existsSync","mkdirSync","resolve","chalk","existsSync","readFileSync","writeFileSync","dirname","resolve","resolve","existsSync","chalk","mkdirSync","chalk","chalk","chalk"]}
package/dist/index.d.ts CHANGED
@@ -1,8 +1,15 @@
1
1
  declare function runDoctor(): Promise<void>;
2
2
 
3
3
  interface NewOptions {
4
+ conductEmail?: string;
4
5
  dryRun: boolean;
6
+ /** Skip interactive prompts; require --owner/--scopes/--*-email flags. */
7
+ nonInteractive?: boolean;
8
+ owner?: string;
5
9
  profile: string;
10
+ /** Comma-separated list, e.g. "core,docs,ci,deps". */
11
+ scopes?: string;
12
+ securityEmail?: string;
6
13
  }
7
14
  declare function runNew(repoName: string, opts: NewOptions): Promise<void>;
8
15
 
@@ -53,7 +60,7 @@ declare const DEFAULT_MANIFEST_FIELDS: {
53
60
  visibility: "oss";
54
61
  };
55
62
  /** Template gate values — `required_when` in templates.manifest.yml. */
56
- type RequiredWhen = 'always' | 'oss' | 'private' | 'has_site' | 'has_packages' | 'publishes_to_npm';
63
+ type RequiredWhen = 'always' | 'never' | 'oss' | 'private' | 'has_site' | 'has_packages' | 'publishes_to_npm';
57
64
  /** Returns true when a template's `required_when` gate applies to this manifest. */
58
65
  declare function isRequired(when: RequiredWhen, manifest: PrecisaManifest): boolean;
59
66
  /**
package/dist/index.js CHANGED
@@ -73,6 +73,8 @@ function isRequired(when, manifest) {
73
73
  switch (when) {
74
74
  case "always":
75
75
  return true;
76
+ case "never":
77
+ return false;
76
78
  case "oss":
77
79
  return manifest.visibility === "oss";
78
80
  case "private":
@@ -90,6 +92,11 @@ function isRequired(when, manifest) {
90
92
  function tokenContext(manifest) {
91
93
  return {
92
94
  COMMIT_SCOPES: manifest.commitScopes.join(","),
95
+ // For human-readable docs (AGENTS.md, README) — backticked list.
96
+ COMMIT_SCOPES_HUMAN: manifest.commitScopes.map((s) => `\`${s}\``).join(", "),
97
+ // For `.commitlintrc.cjs` — scope array spelled as JS string literals,
98
+ // ready to drop inside `[ ... ]`.
99
+ COMMIT_SCOPES_JSON: manifest.commitScopes.map((s) => `'${s}'`).join(", "),
93
100
  CONDUCT_EMAIL: manifest.contactEmails.conduct,
94
101
  HAS_PACKAGES: String(manifest.hasPackages),
95
102
  HAS_SITE: String(manifest.hasSite),
@@ -225,15 +232,22 @@ ${err.message}`));
225
232
  if (errors > 0) process.exit(1);
226
233
  }
227
234
  function compareContent(entry, current, rendered) {
235
+ if (current === rendered) {
236
+ return { message: "", severity: "ok", target: entry.target };
237
+ }
228
238
  if (entry.merge_strategy === "preserve") {
229
239
  return {
230
- message: current === rendered ? "" : "Differs from template (preserve strategy \u2014 suggestion only)",
231
- severity: current === rendered ? "ok" : "info",
240
+ message: "Differs from template (preserve strategy \u2014 suggestion only)",
241
+ severity: "info",
232
242
  target: entry.target
233
243
  };
234
244
  }
235
- if (current === rendered) {
236
- return { message: "", severity: "ok", target: entry.target };
245
+ if (entry.merge_strategy === "skip_if_exists") {
246
+ return {
247
+ message: "Differs from template (scaffold-only; sync will not overwrite)",
248
+ severity: "info",
249
+ target: entry.target
250
+ };
237
251
  }
238
252
  return {
239
253
  message: "Differs from template (run `precisa sync` to update)",
@@ -386,28 +400,33 @@ precisa new ${repoName}`));
386
400
  console.log(chalk2.dim(`profile: ${opts.profile}${opts.dryRun ? " (dry-run)" : ""}`));
387
401
  console.log(chalk2.dim(`target: ${targetDir}
388
402
  `));
389
- const answers = await prompts(
403
+ const answers = opts.nonInteractive ? {
404
+ commitScopes: (opts.scopes ?? "docs,ci,deps").split(",").map((s) => s.trim()).filter(Boolean),
405
+ conductEmail: opts.conductEmail ?? "conduct@precisa-saude.com.br",
406
+ owner: opts.owner ?? "Precisa-Saude",
407
+ securityEmail: opts.securityEmail ?? "security@precisa-saude.com.br"
408
+ } : await prompts(
390
409
  [
391
410
  {
392
- initial: "Precisa-Saude",
411
+ initial: opts.owner ?? "Precisa-Saude",
393
412
  message: "GitHub owner (org or user):",
394
413
  name: "owner",
395
414
  type: "text"
396
415
  },
397
416
  {
398
- initial: "security@precisa-saude.com.br",
417
+ initial: opts.securityEmail ?? "security@precisa-saude.com.br",
399
418
  message: "Security contact email:",
400
419
  name: "securityEmail",
401
420
  type: "text"
402
421
  },
403
422
  {
404
- initial: "conduct@precisa-saude.com.br",
423
+ initial: opts.conductEmail ?? "conduct@precisa-saude.com.br",
405
424
  message: "Code-of-conduct contact email:",
406
425
  name: "conductEmail",
407
426
  type: "text"
408
427
  },
409
428
  {
410
- initial: "docs,ci,deps",
429
+ initial: opts.scopes ?? "docs,ci,deps",
411
430
  message: "Commit scopes (comma-separated):",
412
431
  name: "commitScopes",
413
432
  separator: ",",