safeword 0.6.3 → 0.6.5

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 (55) hide show
  1. package/dist/{check-PECCGHEA.js → check-OYYSYHFP.js} +41 -23
  2. package/dist/check-OYYSYHFP.js.map +1 -0
  3. package/dist/chunk-LNSEDZIW.js +454 -0
  4. package/dist/chunk-LNSEDZIW.js.map +1 -0
  5. package/dist/chunk-ZS3Z3Q37.js +729 -0
  6. package/dist/chunk-ZS3Z3Q37.js.map +1 -0
  7. package/dist/cli.js +7 -7
  8. package/dist/cli.js.map +1 -1
  9. package/dist/diff-325TIZ63.js +168 -0
  10. package/dist/diff-325TIZ63.js.map +1 -0
  11. package/dist/reset-ZGJIKMUW.js +74 -0
  12. package/dist/reset-ZGJIKMUW.js.map +1 -0
  13. package/dist/setup-GAMXTFM2.js +103 -0
  14. package/dist/setup-GAMXTFM2.js.map +1 -0
  15. package/dist/{sync-4XBMKLXS.js → sync-BFMXZEHM.js} +33 -32
  16. package/dist/sync-BFMXZEHM.js.map +1 -0
  17. package/dist/upgrade-X4GREJXN.js +73 -0
  18. package/dist/upgrade-X4GREJXN.js.map +1 -0
  19. package/package.json +15 -14
  20. package/templates/SAFEWORD.md +101 -689
  21. package/templates/guides/architecture-guide.md +1 -1
  22. package/templates/guides/cli-reference.md +35 -0
  23. package/templates/guides/code-philosophy.md +22 -19
  24. package/templates/guides/context-files-guide.md +2 -2
  25. package/templates/guides/data-architecture-guide.md +1 -1
  26. package/templates/guides/design-doc-guide.md +1 -1
  27. package/templates/guides/{testing-methodology.md → development-workflow.md} +1 -1
  28. package/templates/guides/learning-extraction.md +1 -1
  29. package/templates/guides/{llm-instruction-design.md → llm-guide.md} +93 -29
  30. package/templates/guides/tdd-best-practices.md +2 -2
  31. package/templates/guides/test-definitions-guide.md +1 -1
  32. package/templates/guides/user-story-guide.md +1 -1
  33. package/templates/guides/zombie-process-cleanup.md +1 -1
  34. package/dist/check-PECCGHEA.js.map +0 -1
  35. package/dist/chunk-6CVTH67L.js +0 -43
  36. package/dist/chunk-6CVTH67L.js.map +0 -1
  37. package/dist/chunk-75FKNZUM.js +0 -15
  38. package/dist/chunk-75FKNZUM.js.map +0 -1
  39. package/dist/chunk-ARIAOK2F.js +0 -110
  40. package/dist/chunk-ARIAOK2F.js.map +0 -1
  41. package/dist/chunk-FRPJITGG.js +0 -35
  42. package/dist/chunk-FRPJITGG.js.map +0 -1
  43. package/dist/chunk-IWWBZVHT.js +0 -274
  44. package/dist/chunk-IWWBZVHT.js.map +0 -1
  45. package/dist/diff-ZACVJKOU.js +0 -171
  46. package/dist/diff-ZACVJKOU.js.map +0 -1
  47. package/dist/reset-5SRM3P6J.js +0 -145
  48. package/dist/reset-5SRM3P6J.js.map +0 -1
  49. package/dist/setup-65EVU5OT.js +0 -437
  50. package/dist/setup-65EVU5OT.js.map +0 -1
  51. package/dist/sync-4XBMKLXS.js.map +0 -1
  52. package/dist/upgrade-P3WX3ODU.js +0 -153
  53. package/dist/upgrade-P3WX3ODU.js.map +0 -1
  54. package/templates/guides/llm-prompting.md +0 -102
  55. /package/templates/prompts/{review.md → quality-review.md} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/sync.ts"],"sourcesContent":["/**\n * Sync command - Keep linting plugins in sync with project dependencies\n *\n * Detects frameworks in package.json and ensures the corresponding ESLint plugins\n * are installed. Designed to be called from Husky pre-commit hook.\n *\n * Behavior:\n * - Fast exit when nothing needs to change\n * - Installs missing plugins\n * - Optionally stages modified files (--stage flag for pre-commit)\n * - Clear error message if installation fails\n */\n\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { exists, readJson } from '../utils/fs.js';\nimport { detectProjectType, type PackageJson, type ProjectType } from '../utils/project-detector.js';\n\nexport interface SyncOptions {\n quiet?: boolean;\n stage?: boolean;\n}\n\n/**\n * Maps project type flags to their required ESLint plugin packages\n */\nfunction getRequiredPlugins(projectType: ProjectType): string[] {\n const plugins: string[] = [\n // Always required (base plugins)\n 'eslint',\n '@eslint/js',\n 'eslint-plugin-import-x',\n 'eslint-plugin-sonarjs',\n '@microsoft/eslint-plugin-sdl',\n 'eslint-config-prettier',\n 'eslint-plugin-boundaries',\n 'eslint-plugin-playwright',\n ];\n\n // Framework plugins (detected at runtime by dynamic ESLint config)\n if (projectType.typescript) {\n plugins.push('typescript-eslint');\n }\n if (projectType.react || projectType.nextjs) {\n plugins.push('eslint-plugin-react', 'eslint-plugin-react-hooks', 'eslint-plugin-jsx-a11y');\n }\n if (projectType.nextjs) {\n plugins.push('@next/eslint-plugin-next');\n }\n if (projectType.astro) {\n plugins.push('eslint-plugin-astro');\n }\n if (projectType.vue) {\n plugins.push('eslint-plugin-vue');\n }\n if (projectType.svelte) {\n plugins.push('eslint-plugin-svelte');\n }\n if (projectType.electron) {\n plugins.push('@electron-toolkit/eslint-config');\n }\n if (projectType.vitest) {\n plugins.push('@vitest/eslint-plugin');\n }\n\n return plugins;\n}\n\n/**\n * Check which packages are missing from devDependencies\n */\nfunction getMissingPackages(required: string[], installed: Record<string, string>): string[] {\n return required.filter(pkg => !(pkg in installed));\n}\n\n/**\n * Sync linting configuration with current project dependencies\n */\nexport async function sync(options: SyncOptions = {}): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n const packageJsonPath = join(cwd, 'package.json');\n\n // Must be in a safeword project\n if (!exists(safewordDir)) {\n if (!options.quiet) {\n console.error('Not a safeword project. Run `safeword setup` first.');\n }\n process.exit(1);\n }\n\n if (!exists(packageJsonPath)) {\n if (!options.quiet) {\n console.error('No package.json found.');\n }\n process.exit(1);\n }\n\n const packageJson = readJson<PackageJson>(packageJsonPath);\n if (!packageJson) {\n process.exit(1);\n }\n\n // Detect current project type\n const projectType = detectProjectType(packageJson);\n const devDeps = packageJson.devDependencies || {};\n\n // Check for missing plugins\n const requiredPlugins = getRequiredPlugins(projectType);\n const missingPlugins = getMissingPackages(requiredPlugins, devDeps);\n\n // Fast exit if nothing to install\n if (missingPlugins.length === 0) {\n return;\n }\n\n // Install missing plugins\n if (!options.quiet) {\n console.log(`Installing missing ESLint plugins: ${missingPlugins.join(', ')}`);\n }\n\n try {\n execSync(`npm install -D ${missingPlugins.join(' ')}`, {\n cwd,\n stdio: options.quiet ? 'pipe' : 'inherit',\n });\n } catch (error) {\n // Clear error message for network/install failures\n const pluginList = missingPlugins.join(' ');\n console.error(`\\n✗ Failed to install ESLint plugins\\n`);\n console.error(`Your project needs: ${pluginList}`);\n console.error(`\\nRun manually when online:`);\n console.error(` npm install -D ${pluginList}\\n`);\n process.exit(1);\n }\n\n // Stage modified files if --stage flag is set (for pre-commit hook)\n if (options.stage) {\n try {\n execSync('git add package.json package-lock.json', {\n cwd,\n stdio: 'pipe',\n });\n } catch {\n // Not in a git repo or git add failed - ignore\n }\n }\n\n if (!options.quiet) {\n console.log(`✓ Installed ${missingPlugins.length} ESLint plugin(s)`);\n }\n}\n"],"mappings":";;;;;;;;;AAaA,SAAS,YAAY;AACrB,SAAS,gBAAgB;AAYzB,SAAS,mBAAmB,aAAoC;AAC9D,QAAM,UAAoB;AAAA;AAAA,IAExB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAGA,MAAI,YAAY,YAAY;AAC1B,YAAQ,KAAK,mBAAmB;AAAA,EAClC;AACA,MAAI,YAAY,SAAS,YAAY,QAAQ;AAC3C,YAAQ,KAAK,uBAAuB,6BAA6B,wBAAwB;AAAA,EAC3F;AACA,MAAI,YAAY,QAAQ;AACtB,YAAQ,KAAK,0BAA0B;AAAA,EACzC;AACA,MAAI,YAAY,OAAO;AACrB,YAAQ,KAAK,qBAAqB;AAAA,EACpC;AACA,MAAI,YAAY,KAAK;AACnB,YAAQ,KAAK,mBAAmB;AAAA,EAClC;AACA,MAAI,YAAY,QAAQ;AACtB,YAAQ,KAAK,sBAAsB;AAAA,EACrC;AACA,MAAI,YAAY,UAAU;AACxB,YAAQ,KAAK,iCAAiC;AAAA,EAChD;AACA,MAAI,YAAY,QAAQ;AACtB,YAAQ,KAAK,uBAAuB;AAAA,EACtC;AAEA,SAAO;AACT;AAKA,SAAS,mBAAmB,UAAoB,WAA6C;AAC3F,SAAO,SAAS,OAAO,SAAO,EAAE,OAAO,UAAU;AACnD;AAKA,eAAsB,KAAK,UAAuB,CAAC,GAAkB;AACnE,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,cAAc,KAAK,KAAK,WAAW;AACzC,QAAM,kBAAkB,KAAK,KAAK,cAAc;AAGhD,MAAI,CAAC,OAAO,WAAW,GAAG;AACxB,QAAI,CAAC,QAAQ,OAAO;AAClB,cAAQ,MAAM,qDAAqD;AAAA,IACrE;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,CAAC,OAAO,eAAe,GAAG;AAC5B,QAAI,CAAC,QAAQ,OAAO;AAClB,cAAQ,MAAM,wBAAwB;AAAA,IACxC;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,cAAc,SAAsB,eAAe;AACzD,MAAI,CAAC,aAAa;AAChB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,cAAc,kBAAkB,WAAW;AACjD,QAAM,UAAU,YAAY,mBAAmB,CAAC;AAGhD,QAAM,kBAAkB,mBAAmB,WAAW;AACtD,QAAM,iBAAiB,mBAAmB,iBAAiB,OAAO;AAGlE,MAAI,eAAe,WAAW,GAAG;AAC/B;AAAA,EACF;AAGA,MAAI,CAAC,QAAQ,OAAO;AAClB,YAAQ,IAAI,sCAAsC,eAAe,KAAK,IAAI,CAAC,EAAE;AAAA,EAC/E;AAEA,MAAI;AACF,aAAS,kBAAkB,eAAe,KAAK,GAAG,CAAC,IAAI;AAAA,MACrD;AAAA,MACA,OAAO,QAAQ,QAAQ,SAAS;AAAA,IAClC,CAAC;AAAA,EACH,SAAS,OAAO;AAEd,UAAM,aAAa,eAAe,KAAK,GAAG;AAC1C,YAAQ,MAAM;AAAA;AAAA,CAAwC;AACtD,YAAQ,MAAM,uBAAuB,UAAU,EAAE;AACjD,YAAQ,MAAM;AAAA,0BAA6B;AAC3C,YAAQ,MAAM,oBAAoB,UAAU;AAAA,CAAI;AAChD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI,QAAQ,OAAO;AACjB,QAAI;AACF,eAAS,0CAA0C;AAAA,QACjD;AAAA,QACA,OAAO;AAAA,MACT,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ,OAAO;AAClB,YAAQ,IAAI,oBAAe,eAAe,MAAM,mBAAmB;AAAA,EACrE;AACF;","names":[]}
@@ -1,153 +0,0 @@
1
- import {
2
- isGitRepo
3
- } from "./chunk-75FKNZUM.js";
4
- import {
5
- compareVersions
6
- } from "./chunk-W66Z3C5H.js";
7
- import {
8
- VERSION
9
- } from "./chunk-ORQHKDT2.js";
10
- import {
11
- SETTINGS_HOOKS,
12
- ensureAgentsMdLink,
13
- filterOutSafewordHooks
14
- } from "./chunk-IWWBZVHT.js";
15
- import {
16
- error,
17
- header,
18
- info,
19
- listItem,
20
- success
21
- } from "./chunk-FRPJITGG.js";
22
- import {
23
- copyDir,
24
- copyFile,
25
- ensureDir,
26
- exists,
27
- getTemplatesDir,
28
- makeScriptsExecutable,
29
- readFileSafe,
30
- updateJson,
31
- writeFile
32
- } from "./chunk-ARIAOK2F.js";
33
-
34
- // src/commands/upgrade.ts
35
- import { join } from "path";
36
- import { execSync } from "child_process";
37
- async function upgrade() {
38
- const cwd = process.cwd();
39
- const safewordDir = join(cwd, ".safeword");
40
- if (!exists(safewordDir)) {
41
- error("Not configured. Run `safeword setup` first.");
42
- process.exit(1);
43
- }
44
- const versionPath = join(safewordDir, "version");
45
- const projectVersion = readFileSafe(versionPath)?.trim() ?? "0.0.0";
46
- if (compareVersions(VERSION, projectVersion) < 0) {
47
- error(`CLI v${VERSION} is older than project v${projectVersion}.`);
48
- error("Update the CLI first: npm install -g safeword");
49
- process.exit(1);
50
- }
51
- header("Safeword Upgrade");
52
- info(`Upgrading from v${projectVersion} to v${VERSION}`);
53
- const updated = [];
54
- const unchanged = [];
55
- try {
56
- const templatesDir = getTemplatesDir();
57
- info("\nUpdating .safeword directory...");
58
- copyFile(join(templatesDir, "SAFEWORD.md"), join(safewordDir, "SAFEWORD.md"));
59
- writeFile(join(safewordDir, "version"), VERSION);
60
- updated.push(".safeword/SAFEWORD.md");
61
- updated.push(".safeword/version");
62
- copyDir(join(templatesDir, "guides"), join(safewordDir, "guides"));
63
- copyDir(join(templatesDir, "doc-templates"), join(safewordDir, "templates"));
64
- copyDir(join(templatesDir, "prompts"), join(safewordDir, "prompts"));
65
- copyDir(join(templatesDir, "lib"), join(safewordDir, "lib"));
66
- makeScriptsExecutable(join(safewordDir, "lib"));
67
- copyDir(join(templatesDir, "hooks"), join(safewordDir, "hooks"));
68
- makeScriptsExecutable(join(safewordDir, "hooks"));
69
- updated.push(".safeword/guides/");
70
- updated.push(".safeword/templates/");
71
- updated.push(".safeword/prompts/");
72
- updated.push(".safeword/hooks/");
73
- success("Updated .safeword directory");
74
- info("\nVerifying AGENTS.md...");
75
- const agentsMdResult = ensureAgentsMdLink(cwd);
76
- if (agentsMdResult === "created") {
77
- updated.push("AGENTS.md");
78
- success("Created AGENTS.md");
79
- } else if (agentsMdResult === "modified") {
80
- updated.push("AGENTS.md");
81
- success("Restored link to AGENTS.md");
82
- } else {
83
- unchanged.push("AGENTS.md");
84
- info("AGENTS.md link is present");
85
- }
86
- info("\nUpdating Claude Code hooks...");
87
- const claudeDir = join(cwd, ".claude");
88
- const settingsPath = join(claudeDir, "settings.json");
89
- ensureDir(claudeDir);
90
- updateJson(settingsPath, (existing) => {
91
- const hooks = existing?.hooks ?? {};
92
- for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {
93
- const existingHooks = hooks[event] ?? [];
94
- const nonSafewordHooks = filterOutSafewordHooks(existingHooks);
95
- hooks[event] = [...nonSafewordHooks, ...newHooks];
96
- }
97
- return { ...existing, hooks };
98
- });
99
- updated.push(".claude/settings.json");
100
- success("Updated hooks in .claude/settings.json");
101
- info("\nUpdating skills and commands...");
102
- copyDir(join(templatesDir, "skills"), join(claudeDir, "skills"));
103
- copyDir(join(templatesDir, "commands"), join(claudeDir, "commands"));
104
- updated.push(".claude/skills/");
105
- updated.push(".claude/commands/");
106
- success("Updated skills and commands");
107
- if (isGitRepo(cwd)) {
108
- const huskyDir = join(cwd, ".husky");
109
- if (exists(huskyDir)) {
110
- info("\nUpdating Husky pre-commit hook...");
111
- const huskyPreCommit = join(huskyDir, "pre-commit");
112
- writeFile(huskyPreCommit, "npx lint-staged\n");
113
- updated.push(".husky/pre-commit");
114
- success("Updated Husky pre-commit hook");
115
- } else {
116
- info("\nInitializing Husky...");
117
- try {
118
- execSync("npx husky init", { cwd, stdio: "pipe" });
119
- const huskyPreCommit = join(cwd, ".husky", "pre-commit");
120
- writeFile(huskyPreCommit, "npx lint-staged\n");
121
- updated.push(".husky/pre-commit");
122
- success("Initialized Husky with lint-staged");
123
- } catch {
124
- info("Husky not initialized (run: npx husky init)");
125
- }
126
- }
127
- }
128
- header("Upgrade Complete");
129
- info(`
130
- Version: v${projectVersion} \u2192 v${VERSION}`);
131
- if (updated.length > 0) {
132
- info("\nUpdated:");
133
- for (const file of updated) {
134
- listItem(file);
135
- }
136
- }
137
- if (unchanged.length > 0) {
138
- info("\nUnchanged:");
139
- for (const file of unchanged) {
140
- listItem(file);
141
- }
142
- }
143
- success(`
144
- Safeword upgraded to v${VERSION}`);
145
- } catch (err) {
146
- error(`Upgrade failed: ${err instanceof Error ? err.message : "Unknown error"}`);
147
- process.exit(1);
148
- }
149
- }
150
- export {
151
- upgrade
152
- };
153
- //# sourceMappingURL=upgrade-P3WX3ODU.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/upgrade.ts"],"sourcesContent":["/**\n * Upgrade command - Update safeword configuration to latest version\n */\n\nimport { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport {\n exists,\n ensureDir,\n writeFile,\n readFileSafe,\n updateJson,\n copyDir,\n copyFile,\n getTemplatesDir,\n makeScriptsExecutable,\n} from '../utils/fs.js';\nimport { info, success, error, header, listItem } from '../utils/output.js';\nimport { isGitRepo } from '../utils/git.js';\nimport { execSync } from 'node:child_process';\nimport { compareVersions } from '../utils/version.js';\nimport { filterOutSafewordHooks } from '../utils/hooks.js';\nimport { ensureAgentsMdLink } from '../utils/agents-md.js';\nimport { SETTINGS_HOOKS } from '../templates/index.js';\n\nexport async function upgrade(): Promise<void> {\n const cwd = process.cwd();\n const safewordDir = join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDir)) {\n error('Not configured. Run `safeword setup` first.');\n process.exit(1);\n }\n\n // Read project version\n const versionPath = join(safewordDir, 'version');\n const projectVersion = readFileSafe(versionPath)?.trim() ?? '0.0.0';\n\n // Check for downgrade\n if (compareVersions(VERSION, projectVersion) < 0) {\n error(`CLI v${VERSION} is older than project v${projectVersion}.`);\n error('Update the CLI first: npm install -g safeword');\n process.exit(1);\n }\n\n header('Safeword Upgrade');\n info(`Upgrading from v${projectVersion} to v${VERSION}`);\n\n const updated: string[] = [];\n const unchanged: string[] = [];\n\n try {\n const templatesDir = getTemplatesDir();\n\n // 1. Update .safeword directory\n info('\\nUpdating .safeword directory...');\n\n // Update core files from templates\n copyFile(join(templatesDir, 'SAFEWORD.md'), join(safewordDir, 'SAFEWORD.md'));\n writeFile(join(safewordDir, 'version'), VERSION);\n updated.push('.safeword/SAFEWORD.md');\n updated.push('.safeword/version');\n\n // Update guides, templates, prompts from templates\n copyDir(join(templatesDir, 'guides'), join(safewordDir, 'guides'));\n copyDir(join(templatesDir, 'doc-templates'), join(safewordDir, 'templates'));\n copyDir(join(templatesDir, 'prompts'), join(safewordDir, 'prompts'));\n\n // Update lib scripts and make executable\n copyDir(join(templatesDir, 'lib'), join(safewordDir, 'lib'));\n makeScriptsExecutable(join(safewordDir, 'lib'));\n\n // Update hook scripts and make executable\n copyDir(join(templatesDir, 'hooks'), join(safewordDir, 'hooks'));\n makeScriptsExecutable(join(safewordDir, 'hooks'));\n\n updated.push('.safeword/guides/');\n updated.push('.safeword/templates/');\n updated.push('.safeword/prompts/');\n updated.push('.safeword/hooks/');\n success('Updated .safeword directory');\n\n // 2. Verify AGENTS.md link\n info('\\nVerifying AGENTS.md...');\n const agentsMdResult = ensureAgentsMdLink(cwd);\n if (agentsMdResult === 'created') {\n updated.push('AGENTS.md');\n success('Created AGENTS.md');\n } else if (agentsMdResult === 'modified') {\n updated.push('AGENTS.md');\n success('Restored link to AGENTS.md');\n } else {\n unchanged.push('AGENTS.md');\n info('AGENTS.md link is present');\n }\n\n // 3. Update Claude Code hooks\n info('\\nUpdating Claude Code hooks...');\n\n const claudeDir = join(cwd, '.claude');\n const settingsPath = join(claudeDir, 'settings.json');\n\n ensureDir(claudeDir);\n\n updateJson<{ hooks?: Record<string, unknown[]> }>(settingsPath, existing => {\n const hooks = existing?.hooks ?? {};\n\n // Merge hooks, preserving existing non-safeword hooks\n for (const [event, newHooks] of Object.entries(SETTINGS_HOOKS)) {\n const existingHooks = (hooks[event] as unknown[]) ?? [];\n const nonSafewordHooks = filterOutSafewordHooks(existingHooks);\n hooks[event] = [...nonSafewordHooks, ...newHooks];\n }\n\n return { ...existing, hooks };\n });\n\n updated.push('.claude/settings.json');\n success('Updated hooks in .claude/settings.json');\n\n // 4. Update skills and commands\n info('\\nUpdating skills and commands...');\n\n copyDir(join(templatesDir, 'skills'), join(claudeDir, 'skills'));\n copyDir(join(templatesDir, 'commands'), join(claudeDir, 'commands'));\n\n updated.push('.claude/skills/');\n updated.push('.claude/commands/');\n success('Updated skills and commands');\n\n // 5. Update Husky hooks if repo exists\n if (isGitRepo(cwd)) {\n const huskyDir = join(cwd, '.husky');\n if (exists(huskyDir)) {\n info('\\nUpdating Husky pre-commit hook...');\n const huskyPreCommit = join(huskyDir, 'pre-commit');\n writeFile(huskyPreCommit, 'npx lint-staged\\n');\n updated.push('.husky/pre-commit');\n success('Updated Husky pre-commit hook');\n } else {\n // Initialize Husky if not present\n info('\\nInitializing Husky...');\n try {\n execSync('npx husky init', { cwd, stdio: 'pipe' });\n const huskyPreCommit = join(cwd, '.husky', 'pre-commit');\n writeFile(huskyPreCommit, 'npx lint-staged\\n');\n updated.push('.husky/pre-commit');\n success('Initialized Husky with lint-staged');\n } catch {\n info('Husky not initialized (run: npx husky init)');\n }\n }\n }\n\n // Print summary\n header('Upgrade Complete');\n\n info(`\\nVersion: v${projectVersion} → v${VERSION}`);\n\n if (updated.length > 0) {\n info('\\nUpdated:');\n for (const file of updated) {\n listItem(file);\n }\n }\n\n if (unchanged.length > 0) {\n info('\\nUnchanged:');\n for (const file of unchanged) {\n listItem(file);\n }\n }\n\n success(`\\nSafeword upgraded to v${VERSION}`);\n } catch (err) {\n error(`Upgrade failed: ${err instanceof Error ? err.message : 'Unknown error'}`);\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,102 +0,0 @@
1
- # LLM Prompting Best Practices
2
-
3
- This guide covers two related topics:
4
-
5
- **Part 1: Prompting LLMs** - How to structure prompts when actively using an LLM (API calls, chat interactions)
6
-
7
- **Part 2: Writing Instructions for LLMs** - How to write documentation that LLMs will read and follow (SAFEWORD.md, CLAUDE.md, testing guides, coding standards)
8
-
9
- ---
10
-
11
- ## Part 1: Prompting LLMs
12
-
13
- ### Prompt Engineering Principles
14
-
15
- **Concrete Examples Over Abstract Rules:**
16
-
17
- - ✅ Good: Show "❌ BAD" vs "✅ GOOD" code examples
18
- - ❌ Bad: "Follow best practices" (too vague)
19
-
20
- **"Why" Over "What":**
21
-
22
- - Explain architectural trade-offs and reasoning
23
- - Include specific numbers (90% cost reduction, 3x faster)
24
- - Document gotchas with explanations
25
-
26
- **Structured Outputs:**
27
-
28
- - Use JSON mode for predictable LLM responses
29
- - Define explicit schemas with validation
30
- - Return structured data, not prose
31
-
32
- ```typescript
33
- // ❌ BAD - Prose output
34
- "The user wants to create a campaign named 'Shadows' with 4 players"
35
-
36
- // ✅ GOOD - Structured JSON
37
- { "intent": "create_campaign", "name": "Shadows", "playerCount": 4 }
38
- ```
39
-
40
- ### Cost Optimization
41
-
42
- **Prompt Caching (Critical for AI Agents):**
43
-
44
- - Static rules → System prompt with cache_control: ephemeral (caches for ~5 min, auto-expires)
45
- - Dynamic data (character state, user input) → User message (no caching)
46
- - Example: 468-line prompt costs $0.10 without caching, $0.01 with (90% reduction)
47
- - Cache invalidation: ANY change to cached blocks breaks ALL caches
48
- - Rule: Change system prompts sparingly; accept one-time cache rebuild cost
49
-
50
- **Message Architecture:**
51
-
52
- ```typescript
53
- // ✅ GOOD - Cacheable system prompt
54
- systemPrompt: [
55
- { text: STATIC_RULES, cache_control: { type: 'ephemeral' } },
56
- { text: STATIC_EXAMPLES, cache_control: { type: 'ephemeral' } },
57
- ];
58
- userMessage: `Character: ${dynamicState}\nAction: ${userInput}`;
59
-
60
- // ❌ BAD - Uncacheable (character state in system prompt)
61
- systemPrompt: `Rules + Character: ${dynamicState}`;
62
- ```
63
-
64
- ### Testing AI Outputs
65
-
66
- **LLM-as-Judge Pattern:**
67
-
68
- - Use LLM to evaluate nuanced qualities (narrative tone, reasoning quality)
69
- - Avoid brittle keyword matching for creative outputs
70
- - Define rubrics: EXCELLENT / ACCEPTABLE / POOR with criteria
71
- - Example: "Does the GM's response show collaborative tone?" vs checking for specific words
72
-
73
- **Evaluation Framework:**
74
-
75
- - Unit tests: Pure functions (parsing, validation)
76
- - Integration tests: Agent + real LLM calls (schema compliance)
77
- - LLM Evals: Judgment quality (position/effect reasoning, atmosphere)
78
- - Cost awareness: 30 scenarios ≈ $0.15-0.30 per run with caching
79
-
80
- ---
81
-
82
- ## Part 2: Writing Instructions for LLMs
83
-
84
- **Comprehensive framework:** See @.safeword/guides/llm-instruction-design.md
85
-
86
- **Quick summary:** When creating documentation that LLMs will read and follow (SAFEWORD.md, CLAUDE.md, testing guides, coding standards), apply 13 core principles:
87
-
88
- 1. **MECE Principle** - Decision trees must be mutually exclusive and collectively exhaustive
89
- 2. **Explicit Over Implicit** - Define all terms, never assume LLMs know what you mean
90
- 3. **No Contradictions** - Different sections must align, LLMs don't reconcile conflicts
91
- 4. **Concrete Examples Over Abstract Rules** - Show, don't just tell (2-3 examples per rule)
92
- 5. **Edge Cases Must Be Explicit** - What seems obvious to humans often isn't to LLMs
93
- 6. **Actionable Over Vague** - Replace subjective terms with optimization rules + red flags
94
- 7. **Decision Trees: Sequential Over Parallel** - Ordered steps that stop at first match
95
- 8. **Tie-Breaking Rules** - Tell LLMs how to choose when multiple options apply
96
- 9. **Lookup Tables for Complex Decisions** - Provide reference tables for complex logic
97
- 10. **Avoid Caveats in Tables** - Keep patterns clean, parentheticals break LLM pattern matching
98
- 11. **Percentages: Context or None** - Include adjustment guidance or use principles instead
99
- 12. **Specificity in Questions** - Use precise technical terms, not general descriptions
100
- 13. **Re-evaluation Paths** - Provide concrete next steps when LLMs hit dead ends
101
-
102
- **Also includes:** Anti-patterns to avoid, quality checklist, research-backed principles, and before/after examples.