safeword 0.6.9 → 0.7.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 (56) hide show
  1. package/dist/{check-OYYSYHFP.js → check-QGZJ62PY.js} +73 -57
  2. package/dist/check-QGZJ62PY.js.map +1 -0
  3. package/dist/{chunk-ZS3Z3Q37.js → chunk-QPO3C3FP.js} +285 -65
  4. package/dist/chunk-QPO3C3FP.js.map +1 -0
  5. package/dist/{chunk-LNSEDZIW.js → chunk-YMLVQC4V.js} +159 -152
  6. package/dist/chunk-YMLVQC4V.js.map +1 -0
  7. package/dist/cli.js +6 -6
  8. package/dist/{diff-325TIZ63.js → diff-654SSCFQ.js} +51 -53
  9. package/dist/diff-654SSCFQ.js.map +1 -0
  10. package/dist/index.d.ts +1 -0
  11. package/dist/{reset-ZGJIKMUW.js → reset-IU6AIT7C.js} +3 -3
  12. package/dist/{setup-GAMXTFM2.js → setup-5IQ4KV2M.js} +17 -20
  13. package/dist/setup-5IQ4KV2M.js.map +1 -0
  14. package/dist/{sync-BFMXZEHM.js → sync-YBQEISFI.js} +8 -26
  15. package/dist/sync-YBQEISFI.js.map +1 -0
  16. package/dist/{upgrade-X4GREJXN.js → upgrade-RRTWEQIP.js} +3 -3
  17. package/package.json +1 -1
  18. package/templates/SAFEWORD.md +52 -15
  19. package/templates/commands/architecture.md +1 -1
  20. package/templates/commands/lint.md +1 -0
  21. package/templates/commands/quality-review.md +1 -1
  22. package/templates/cursor/rules/safeword-core.mdc +5 -0
  23. package/templates/doc-templates/architecture-template.md +1 -1
  24. package/templates/doc-templates/task-spec-template.md +151 -0
  25. package/templates/doc-templates/ticket-template.md +2 -4
  26. package/templates/guides/architecture-guide.md +2 -2
  27. package/templates/guides/code-philosophy.md +1 -1
  28. package/templates/guides/context-files-guide.md +3 -3
  29. package/templates/guides/design-doc-guide.md +2 -2
  30. package/templates/guides/development-workflow.md +2 -2
  31. package/templates/guides/learning-extraction.md +9 -9
  32. package/templates/guides/tdd-best-practices.md +39 -38
  33. package/templates/guides/test-definitions-guide.md +15 -14
  34. package/templates/hooks/cursor/after-file-edit.sh +66 -0
  35. package/templates/hooks/cursor/stop.sh +50 -0
  36. package/templates/hooks/post-tool-lint.sh +19 -5
  37. package/templates/hooks/prompt-questions.sh +1 -1
  38. package/templates/hooks/session-lint-check.sh +1 -1
  39. package/templates/hooks/session-verify-agents.sh +1 -1
  40. package/templates/hooks/session-version.sh +1 -1
  41. package/templates/hooks/stop-quality.sh +1 -1
  42. package/templates/markdownlint-cli2.jsonc +18 -19
  43. package/templates/scripts/bisect-test-pollution.sh +87 -0
  44. package/templates/scripts/bisect-zombie-processes.sh +129 -0
  45. package/templates/scripts/lint-md.sh +16 -0
  46. package/templates/skills/safeword-quality-reviewer/SKILL.md +3 -3
  47. package/templates/skills/safeword-systematic-debugger/SKILL.md +246 -0
  48. package/templates/skills/safeword-tdd-enforcer/SKILL.md +221 -0
  49. package/dist/check-OYYSYHFP.js.map +0 -1
  50. package/dist/chunk-LNSEDZIW.js.map +0 -1
  51. package/dist/chunk-ZS3Z3Q37.js.map +0 -1
  52. package/dist/diff-325TIZ63.js.map +0 -1
  53. package/dist/setup-GAMXTFM2.js.map +0 -1
  54. package/dist/sync-BFMXZEHM.js.map +0 -1
  55. /package/dist/{reset-ZGJIKMUW.js.map → reset-IU6AIT7C.js.map} +0 -0
  56. /package/dist/{upgrade-X4GREJXN.js.map → upgrade-RRTWEQIP.js.map} +0 -0
@@ -9,18 +9,43 @@ import {
9
9
  reconcile,
10
10
  success,
11
11
  warn
12
- } from "./chunk-LNSEDZIW.js";
12
+ } from "./chunk-YMLVQC4V.js";
13
13
  import {
14
14
  SAFEWORD_SCHEMA,
15
15
  exists,
16
16
  readFileSafe
17
- } from "./chunk-ZS3Z3Q37.js";
17
+ } from "./chunk-QPO3C3FP.js";
18
18
  import {
19
19
  VERSION
20
20
  } from "./chunk-ORQHKDT2.js";
21
21
 
22
22
  // src/commands/check.ts
23
23
  import { join } from "path";
24
+ function findMissingFiles(cwd, actions) {
25
+ const issues = [];
26
+ for (const action of actions) {
27
+ if (action.type === "write" && !exists(join(cwd, action.path))) {
28
+ issues.push(`Missing: ${action.path}`);
29
+ }
30
+ }
31
+ return issues;
32
+ }
33
+ function findMissingPatches(cwd, actions) {
34
+ const issues = [];
35
+ for (const action of actions) {
36
+ if (action.type !== "text-patch") continue;
37
+ const fullPath = join(cwd, action.path);
38
+ if (!exists(fullPath)) {
39
+ issues.push(`${action.path} file missing`);
40
+ } else {
41
+ const content = readFileSafe(fullPath) ?? "";
42
+ if (action.definition && !content.includes(action.definition.marker)) {
43
+ issues.push(`${action.path} missing safeword link`);
44
+ }
45
+ }
46
+ }
47
+ return issues;
48
+ }
24
49
  async function checkLatestVersion(timeout = 3e3) {
25
50
  try {
26
51
  const controller = new AbortController();
@@ -53,32 +78,11 @@ async function checkHealth(cwd) {
53
78
  const projectVersion = readFileSafe(versionPath)?.trim() ?? null;
54
79
  const ctx = createProjectContext(cwd);
55
80
  const result = await reconcile(SAFEWORD_SCHEMA, "upgrade", ctx, { dryRun: true });
56
- const issues = [];
57
- const writeActions = result.actions.filter((a) => a.type === "write");
58
- for (const action of writeActions) {
59
- if (action.type === "write") {
60
- const fullPath = join(cwd, action.path);
61
- if (!exists(fullPath)) {
62
- issues.push(`Missing: ${action.path}`);
63
- }
64
- }
65
- }
66
- const textPatchActions = result.actions.filter((a) => a.type === "text-patch");
67
- for (const action of textPatchActions) {
68
- if (action.type === "text-patch") {
69
- const fullPath = join(cwd, action.path);
70
- if (!exists(fullPath)) {
71
- issues.push(`${action.path} file missing`);
72
- } else {
73
- const content = readFileSafe(fullPath) ?? "";
74
- if (!content.includes(action.definition.marker)) {
75
- issues.push(`${action.path} missing safeword link`);
76
- }
77
- }
78
- }
79
- }
80
- const settingsPath = join(cwd, ".claude", "settings.json");
81
- if (!exists(settingsPath)) {
81
+ const issues = [
82
+ ...findMissingFiles(cwd, result.actions),
83
+ ...findMissingPatches(cwd, result.actions)
84
+ ];
85
+ if (!exists(join(cwd, ".claude", "settings.json"))) {
82
86
  issues.push("Missing: .claude/settings.json");
83
87
  }
84
88
  return {
@@ -91,59 +95,71 @@ async function checkHealth(cwd) {
91
95
  missingPackages: result.packagesToInstall
92
96
  };
93
97
  }
94
- async function check(options) {
95
- const cwd = process.cwd();
96
- header("Safeword Health Check");
97
- const health = await checkHealth(cwd);
98
- if (!health.configured) {
99
- info("Not configured. Run `safeword setup` to initialize.");
98
+ async function reportUpdateStatus(health) {
99
+ info("\nChecking for updates...");
100
+ const latestVersion = await checkLatestVersion();
101
+ if (!latestVersion) {
102
+ warn("Couldn't check for updates (offline?)");
100
103
  return;
101
104
  }
102
- keyValue("Safeword CLI", `v${health.cliVersion}`);
103
- keyValue("Project config", health.projectVersion ? `v${health.projectVersion}` : "unknown");
104
- if (!options.offline) {
105
- info("\nChecking for updates...");
106
- const latestVersion = await checkLatestVersion();
107
- if (latestVersion) {
108
- health.latestVersion = latestVersion;
109
- health.updateAvailable = isNewerVersion(health.cliVersion, latestVersion);
110
- if (health.updateAvailable) {
111
- warn(`Update available: v${latestVersion}`);
112
- info("Run `npm install -g safeword` to upgrade");
113
- } else {
114
- success("CLI is up to date");
115
- }
116
- } else {
117
- warn("Couldn't check for updates (offline?)");
118
- }
105
+ health.latestVersion = latestVersion;
106
+ health.updateAvailable = isNewerVersion(health.cliVersion, latestVersion);
107
+ if (health.updateAvailable) {
108
+ warn(`Update available: v${latestVersion}`);
109
+ info("Run `npm install -g safeword` to upgrade");
119
110
  } else {
120
- info("\nSkipped update check (offline mode)");
111
+ success("CLI is up to date");
121
112
  }
122
- if (health.projectVersion && isNewerVersion(health.cliVersion, health.projectVersion)) {
113
+ }
114
+ function reportVersionMismatch(health) {
115
+ if (!health.projectVersion) return;
116
+ if (isNewerVersion(health.cliVersion, health.projectVersion)) {
123
117
  warn(`Project config (v${health.projectVersion}) is newer than CLI (v${health.cliVersion})`);
124
118
  info("Consider upgrading the CLI");
125
- } else if (health.projectVersion && isNewerVersion(health.projectVersion, health.cliVersion)) {
119
+ } else if (isNewerVersion(health.projectVersion, health.cliVersion)) {
126
120
  info(`
127
121
  Upgrade available for project config`);
128
122
  info(
129
123
  `Run \`safeword upgrade\` to update from v${health.projectVersion} to v${health.cliVersion}`
130
124
  );
131
125
  }
126
+ }
127
+ function reportHealthSummary(health) {
132
128
  if (health.issues.length > 0) {
133
129
  header("Issues Found");
134
130
  for (const issue of health.issues) {
135
131
  warn(issue);
136
132
  }
137
133
  info("\nRun `safeword upgrade` to repair configuration");
138
- } else if (health.missingPackages.length > 0) {
134
+ return;
135
+ }
136
+ if (health.missingPackages.length > 0) {
139
137
  header("Missing Packages");
140
138
  info(`${health.missingPackages.length} linting packages not installed`);
141
139
  info("Run `safeword sync` to install missing packages");
140
+ return;
141
+ }
142
+ success("\nConfiguration is healthy");
143
+ }
144
+ async function check(options) {
145
+ const cwd = process.cwd();
146
+ header("Safeword Health Check");
147
+ const health = await checkHealth(cwd);
148
+ if (!health.configured) {
149
+ info("Not configured. Run `safeword setup` to initialize.");
150
+ return;
151
+ }
152
+ keyValue("Safeword CLI", `v${health.cliVersion}`);
153
+ keyValue("Project config", health.projectVersion ? `v${health.projectVersion}` : "unknown");
154
+ if (options.offline) {
155
+ info("\nSkipped update check (offline mode)");
142
156
  } else {
143
- success("\nConfiguration is healthy");
157
+ await reportUpdateStatus(health);
144
158
  }
159
+ reportVersionMismatch(health);
160
+ reportHealthSummary(health);
145
161
  }
146
162
  export {
147
163
  check
148
164
  };
149
- //# sourceMappingURL=check-OYYSYHFP.js.map
165
+ //# sourceMappingURL=check-QGZJ62PY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/check.ts"],"sourcesContent":["/**\n * Check command - Verify project health and configuration\n *\n * Uses reconcile() with dryRun to detect missing files and configuration issues.\n */\n\nimport { join } from 'node:path';\nimport { VERSION } from '../version.js';\nimport { exists, readFileSafe } from '../utils/fs.js';\nimport { info, success, warn, header, keyValue } from '../utils/output.js';\nimport { isNewerVersion } from '../utils/version.js';\nimport { createProjectContext } from '../utils/context.js';\nimport { reconcile } from '../reconcile.js';\nimport { SAFEWORD_SCHEMA } from '../schema.js';\n\nexport interface CheckOptions {\n offline?: boolean;\n}\n\n/** Check for missing files from write actions */\nfunction findMissingFiles(cwd: string, actions: { type: string; path: string }[]): string[] {\n const issues: string[] = [];\n for (const action of actions) {\n if (action.type === 'write' && !exists(join(cwd, action.path))) {\n issues.push(`Missing: ${action.path}`);\n }\n }\n return issues;\n}\n\n/** Check for missing text patch markers */\nfunction findMissingPatches(\n cwd: string,\n actions: { type: string; path: string; definition?: { marker: string } }[],\n): string[] {\n const issues: string[] = [];\n for (const action of actions) {\n if (action.type !== 'text-patch') continue;\n\n const fullPath = join(cwd, action.path);\n if (!exists(fullPath)) {\n issues.push(`${action.path} file missing`);\n } else {\n const content = readFileSafe(fullPath) ?? '';\n if (action.definition && !content.includes(action.definition.marker)) {\n issues.push(`${action.path} missing safeword link`);\n }\n }\n }\n return issues;\n}\n\ninterface HealthStatus {\n configured: boolean;\n projectVersion: string | null;\n cliVersion: string;\n updateAvailable: boolean;\n latestVersion: string | null;\n issues: string[];\n missingPackages: string[];\n}\n\n/**\n * Check for latest version from npm (with timeout)\n */\nasync function checkLatestVersion(timeout = 3000): Promise<string | null> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const response = await fetch('https://registry.npmjs.org/safeword/latest', {\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) return null;\n\n const data = (await response.json()) as { version?: string };\n return data.version ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Check project configuration health using reconcile dryRun\n */\nasync function checkHealth(cwd: string): Promise<HealthStatus> {\n const safewordDir = join(cwd, '.safeword');\n\n // Check if configured\n if (!exists(safewordDir)) {\n return {\n configured: false,\n projectVersion: null,\n cliVersion: VERSION,\n updateAvailable: false,\n latestVersion: null,\n issues: [],\n missingPackages: [],\n };\n }\n\n // Read project version\n const versionPath = join(safewordDir, 'version');\n const projectVersion = readFileSafe(versionPath)?.trim() ?? null;\n\n // Use reconcile with dryRun to detect issues\n const ctx = createProjectContext(cwd);\n const result = await reconcile(SAFEWORD_SCHEMA, 'upgrade', ctx, { dryRun: true });\n\n // Collect issues from write actions and text patches\n const issues: string[] = [\n ...findMissingFiles(cwd, result.actions),\n ...findMissingPatches(cwd, result.actions),\n ];\n\n // Check for missing .claude/settings.json\n if (!exists(join(cwd, '.claude', 'settings.json'))) {\n issues.push('Missing: .claude/settings.json');\n }\n\n return {\n configured: true,\n projectVersion,\n cliVersion: VERSION,\n updateAvailable: false,\n latestVersion: null,\n issues,\n missingPackages: result.packagesToInstall,\n };\n}\n\n/** Check for CLI updates and report status */\nasync function reportUpdateStatus(health: HealthStatus): Promise<void> {\n info('\\nChecking for updates...');\n const latestVersion = await checkLatestVersion();\n\n if (!latestVersion) {\n warn(\"Couldn't check for updates (offline?)\");\n return;\n }\n\n health.latestVersion = latestVersion;\n health.updateAvailable = isNewerVersion(health.cliVersion, latestVersion);\n\n if (health.updateAvailable) {\n warn(`Update available: v${latestVersion}`);\n info('Run `npm install -g safeword` to upgrade');\n } else {\n success('CLI is up to date');\n }\n}\n\n/** Compare project version vs CLI version and report */\nfunction reportVersionMismatch(health: HealthStatus): void {\n if (!health.projectVersion) return;\n\n if (isNewerVersion(health.cliVersion, health.projectVersion)) {\n warn(`Project config (v${health.projectVersion}) is newer than CLI (v${health.cliVersion})`);\n info('Consider upgrading the CLI');\n } else if (isNewerVersion(health.projectVersion, health.cliVersion)) {\n info(`\\nUpgrade available for project config`);\n info(\n `Run \\`safeword upgrade\\` to update from v${health.projectVersion} to v${health.cliVersion}`,\n );\n }\n}\n\n/** Report issues or success */\nfunction reportHealthSummary(health: HealthStatus): void {\n if (health.issues.length > 0) {\n header('Issues Found');\n for (const issue of health.issues) {\n warn(issue);\n }\n info('\\nRun `safeword upgrade` to repair configuration');\n return;\n }\n\n if (health.missingPackages.length > 0) {\n header('Missing Packages');\n info(`${health.missingPackages.length} linting packages not installed`);\n info('Run `safeword sync` to install missing packages');\n return;\n }\n\n success('\\nConfiguration is healthy');\n}\n\nexport async function check(options: CheckOptions): Promise<void> {\n const cwd = process.cwd();\n\n header('Safeword Health Check');\n\n const health = await checkHealth(cwd);\n\n // Not configured\n if (!health.configured) {\n info('Not configured. Run `safeword setup` to initialize.');\n return;\n }\n\n // Show versions\n keyValue('Safeword CLI', `v${health.cliVersion}`);\n keyValue('Project config', health.projectVersion ? `v${health.projectVersion}` : 'unknown');\n\n // Check for updates (unless offline)\n if (options.offline) {\n info('\\nSkipped update check (offline mode)');\n } else {\n await reportUpdateStatus(health);\n }\n\n reportVersionMismatch(health);\n reportHealthSummary(health);\n}\n"],"mappings":"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","names":[]}