gaslighting-engine 0.1.0 → 0.2.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 (66) hide show
  1. package/.agents/prompts/gaslighting.md +32 -0
  2. package/.agents/skills/gaslighting/SKILL.md +101 -0
  3. package/.agents/skills/gaslighting/agents/openai.yaml +8 -0
  4. package/.agents/skills/gaslighting/references/GASLIGHTING_TEMPLATE.md +425 -0
  5. package/.agents/skills/gaslighting/references/HARDCORE_DISCIPLINE_TEMPLATE.md +425 -0
  6. package/.agents/skills/gaslighting/scripts/generate-gaslighting-docs.ts +3 -0
  7. package/.codex/prompts/gaslighting.md +12 -10
  8. package/.codex/skills/gaslighting/SKILL.md +13 -8
  9. package/README.md +60 -10
  10. package/dist/cli.js +13 -2
  11. package/dist/commands/care.js +33 -0
  12. package/dist/commands/codexInstall.js +2 -2
  13. package/dist/commands/doctor.js +17 -11
  14. package/dist/commands/init.js +5 -3
  15. package/dist/commands/update.js +12 -5
  16. package/dist/core/generateDocs.js +54 -12
  17. package/dist/core/generateOtherMarkdown.js +47 -37
  18. package/dist/core/projectCare.js +135 -0
  19. package/dist/index.js +2 -0
  20. package/dist/utils/logger.js +10 -2
  21. package/dist/utils/updateCheck.js +61 -0
  22. package/dist/version.js +2 -0
  23. package/docs/codex-usage.md +4 -4
  24. package/docs/examples.md +14 -0
  25. package/examples/ecommerce/.codex/prompts/gaslighting.md +12 -10
  26. package/examples/ecommerce/.codex/skills/gaslighting/SKILL.md +13 -8
  27. package/examples/ecommerce/.gaslighting/AGENTS.md +49 -0
  28. package/examples/{landing-page → ecommerce/.gaslighting}/CODEX_PROMPT.md +9 -8
  29. package/examples/ecommerce/{MEMORY.md → .gaslighting/MEMORY.md} +2 -2
  30. package/examples/ecommerce/.gaslighting/PROJECT_CARE.md +75 -0
  31. package/examples/ecommerce/AGENTS.md +14 -38
  32. package/examples/hospital-homepage/.codex/prompts/gaslighting.md +12 -10
  33. package/examples/hospital-homepage/.codex/skills/gaslighting/SKILL.md +13 -8
  34. package/examples/hospital-homepage/.gaslighting/AGENTS.md +49 -0
  35. package/examples/{ecommerce → hospital-homepage/.gaslighting}/CODEX_PROMPT.md +9 -8
  36. package/examples/hospital-homepage/{MEMORY.md → .gaslighting/MEMORY.md} +2 -2
  37. package/examples/hospital-homepage/.gaslighting/PRD.md +119 -0
  38. package/examples/hospital-homepage/.gaslighting/PROJECT_CARE.md +75 -0
  39. package/examples/hospital-homepage/AGENTS.md +14 -38
  40. package/examples/landing-page/.codex/prompts/gaslighting.md +12 -10
  41. package/examples/landing-page/.codex/skills/gaslighting/SKILL.md +13 -8
  42. package/examples/landing-page/.gaslighting/AGENTS.md +49 -0
  43. package/examples/{hospital-homepage → landing-page/.gaslighting}/CODEX_PROMPT.md +9 -8
  44. package/examples/landing-page/{MEMORY.md → .gaslighting/MEMORY.md} +2 -2
  45. package/examples/landing-page/.gaslighting/PROJECT_CARE.md +74 -0
  46. package/examples/landing-page/.gaslighting/STACK_POLICY.md +64 -0
  47. package/examples/landing-page/AGENTS.md +14 -38
  48. package/package.json +2 -1
  49. /package/{examples/hospital-homepage/PRD.md → .agents/skills/gaslighting/references/HOSPITAL_HOMEPAGE_EXAMPLE.md} +0 -0
  50. /package/{examples/ecommerce/STACK_POLICY.md → .agents/skills/gaslighting/references/STACK_POLICY_TEMPLATE.md} +0 -0
  51. /package/examples/ecommerce/{ASSUMPTIONS.md → .gaslighting/ASSUMPTIONS.md} +0 -0
  52. /package/examples/ecommerce/{DECISION_LOG.md → .gaslighting/DECISION_LOG.md} +0 -0
  53. /package/examples/ecommerce/{GASLIGHTING.md → .gaslighting/GASLIGHTING.md} +0 -0
  54. /package/examples/ecommerce/{MISSING_INFO.md → .gaslighting/MISSING_INFO.md} +0 -0
  55. /package/examples/ecommerce/{PRD.md → .gaslighting/PRD.md} +0 -0
  56. /package/examples/{hospital-homepage → ecommerce/.gaslighting}/STACK_POLICY.md +0 -0
  57. /package/examples/hospital-homepage/{ASSUMPTIONS.md → .gaslighting/ASSUMPTIONS.md} +0 -0
  58. /package/examples/hospital-homepage/{DECISION_LOG.md → .gaslighting/DECISION_LOG.md} +0 -0
  59. /package/examples/hospital-homepage/{GASLIGHTING.md → .gaslighting/GASLIGHTING.md} +0 -0
  60. /package/examples/hospital-homepage/{MISSING_INFO.md → .gaslighting/MISSING_INFO.md} +0 -0
  61. /package/examples/{landing-page → hospital-homepage/.gaslighting}/STACK_POLICY.md +0 -0
  62. /package/examples/landing-page/{ASSUMPTIONS.md → .gaslighting/ASSUMPTIONS.md} +0 -0
  63. /package/examples/landing-page/{DECISION_LOG.md → .gaslighting/DECISION_LOG.md} +0 -0
  64. /package/examples/landing-page/{GASLIGHTING.md → .gaslighting/GASLIGHTING.md} +0 -0
  65. /package/examples/landing-page/{MISSING_INFO.md → .gaslighting/MISSING_INFO.md} +0 -0
  66. /package/examples/landing-page/{PRD.md → .gaslighting/PRD.md} +0 -0
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { inspectProjectCare } from "../core/projectCare.js";
3
4
  import { printBanner } from "../utils/banner.js";
4
5
  export function runDoctor(options) {
5
6
  const root = options.path ?? process.cwd();
@@ -7,20 +8,21 @@ export function runDoctor(options) {
7
8
  const full = join(root, file);
8
9
  return existsSync(full) ? readFileSync(full, "utf8") : "";
9
10
  };
10
- const gaslighting = read("GASLIGHTING.md");
11
+ const gaslighting = read(".gaslighting/GASLIGHTING.md") || read("GASLIGHTING.md");
11
12
  const agents = read("AGENTS.md");
12
13
  const checks = [
13
- fileCheck(root, "GASLIGHTING.md"),
14
- fileCheck(root, "PRD.md"),
14
+ fileCheck(root, ".gaslighting/GASLIGHTING.md"),
15
+ fileCheck(root, ".gaslighting/PRD.md"),
15
16
  fileCheck(root, "AGENTS.md"),
16
- fileCheck(root, "STACK_POLICY.md"),
17
- fileCheck(root, "MISSING_INFO.md"),
18
- fileCheck(root, "DECISION_LOG.md"),
19
- fileCheck(root, "MEMORY.md"),
20
- { name: "GASLIGHTING.md contains project purpose", ok: /Project Purpose/i.test(gaslighting) },
21
- { name: "GASLIGHTING.md contains no-fake-completion rules", ok: /No Fake Completion|Fake completion/i.test(gaslighting) },
22
- { name: "GASLIGHTING.md contains full-scope rules", ok: /Full Scope Enforcement|full requested scope/i.test(gaslighting) },
23
- { name: "AGENTS.md points to GASLIGHTING.md", ok: /GASLIGHTING\.md/.test(agents) },
17
+ fileCheck(root, ".gaslighting/STACK_POLICY.md"),
18
+ fileCheck(root, ".gaslighting/MISSING_INFO.md"),
19
+ fileCheck(root, ".gaslighting/DECISION_LOG.md"),
20
+ fileCheck(root, ".gaslighting/MEMORY.md"),
21
+ fileCheck(root, ".gaslighting/PROJECT_CARE.md"),
22
+ { name: ".gaslighting/GASLIGHTING.md contains project purpose", ok: /Project Purpose/i.test(gaslighting) },
23
+ { name: ".gaslighting/GASLIGHTING.md contains no-fake-completion rules", ok: /No Fake Completion|Fake completion/i.test(gaslighting) },
24
+ { name: ".gaslighting/GASLIGHTING.md contains full-scope rules", ok: /Full Scope Enforcement|full requested scope/i.test(gaslighting) },
25
+ { name: "AGENTS.md points to .gaslighting/GASLIGHTING.md", ok: /\.gaslighting\/GASLIGHTING\.md|\.gaslighting\\GASLIGHTING\.md/.test(agents) },
24
26
  fileCheck(root, ".codex/skills/gaslighting/SKILL.md"),
25
27
  fileCheck(root, ".codex/skills/gaslighting/agents/openai.yaml"),
26
28
  fileCheck(root, ".codex/prompts/gaslighting.md"),
@@ -28,6 +30,10 @@ export function runDoctor(options) {
28
30
  printBanner("DOCTOR");
29
31
  for (const check of checks)
30
32
  console.log(`${check.ok ? "PASS" : "FAIL"} ${check.name}`);
33
+ const care = inspectProjectCare(root);
34
+ console.log("\nCare checks:");
35
+ for (const check of care.checks)
36
+ console.log(`${check.ok ? "PASS" : "WARN"} ${check.label}: ${check.message}`);
31
37
  const failed = checks.filter((check) => !check.ok);
32
38
  if (failed.length) {
33
39
  console.log(`\n${failed.length} check(s) failed.`);
@@ -1,8 +1,10 @@
1
1
  import { generateDocs } from "../core/generateDocs.js";
2
2
  import { modeSummary } from "../core/content.js";
3
+ import { inspectProjectCare } from "../core/projectCare.js";
3
4
  import { writeDocs } from "../utils/file.js";
4
5
  import { printSummary } from "../utils/logger.js";
5
6
  export function runInit(rawUserRequest, options) {
7
+ const root = options.path ?? process.cwd();
6
8
  const input = {
7
9
  rawUserRequest,
8
10
  projectType: options.type,
@@ -14,9 +16,9 @@ export function runInit(rawUserRequest, options) {
14
16
  force: options.force,
15
17
  dryRun: options.dryRun,
16
18
  };
17
- const { analysis, docs } = generateDocs(input);
18
- const results = writeDocs(options.path ?? process.cwd(), docs, options.force, options.dryRun);
19
- printSummary(analysis, modeSummary(input), results);
19
+ const { analysis, docs } = generateDocs(input, root);
20
+ const results = writeDocs(root, docs, options.force, options.dryRun);
21
+ printSummary(analysis, modeSummary(input), results, inspectProjectCare(root));
20
22
  if (options.dryRun) {
21
23
  console.log("\nPreviews:");
22
24
  for (const doc of docs) {
@@ -4,14 +4,21 @@ import { printBanner } from "../utils/banner.js";
4
4
  import { isoDate } from "../utils/date.js";
5
5
  export function runUpdate(newInformation, options) {
6
6
  const root = options.path ?? process.cwd();
7
- const files = ["GASLIGHTING.md", "PRD.md", "ASSUMPTIONS.md", "MISSING_INFO.md", "MEMORY.md"];
7
+ const files = [
8
+ ".gaslighting/GASLIGHTING.md",
9
+ ".gaslighting/PRD.md",
10
+ ".gaslighting/ASSUMPTIONS.md",
11
+ ".gaslighting/MISSING_INFO.md",
12
+ ".gaslighting/MEMORY.md",
13
+ ".gaslighting/PROJECT_CARE.md",
14
+ ];
8
15
  const block = `\n\n## Update: ${isoDate()}\n\nNew information:\n\n> ${newInformation}\n\nThis information supersedes conflicting assumptions. Do not silently overwrite old assumptions; treat earlier conflicts as outdated and preserve the change record in DECISION_LOG.md.\n`;
9
16
  if (options.dryRun) {
10
17
  printBanner("UPDATE DRY RUN");
11
18
  console.log("Dry run update would touch:");
12
19
  for (const file of files)
13
20
  console.log(`- ${file}`);
14
- console.log("- DECISION_LOG.md");
21
+ console.log("- .gaslighting/DECISION_LOG.md");
15
22
  return;
16
23
  }
17
24
  for (const file of files) {
@@ -19,17 +26,17 @@ export function runUpdate(newInformation, options) {
19
26
  if (existsSync(path))
20
27
  appendFileSync(path, block, "utf8");
21
28
  }
22
- const decisionPath = join(root, "DECISION_LOG.md");
29
+ const decisionPath = join(root, ".gaslighting/DECISION_LOG.md");
23
30
  const decision = `\n\n## Decision: Update project information\n\nDate: ${isoDate()}\n\n### Decision\n\nRecord new project information:\n\n> ${newInformation}\n\n### Reason\n\nThe project assumptions changed or became more specific.\n\n### Alternatives Considered\n\n- Ignore the new information.\n- Silently overwrite old assumptions.\n\n### Why Alternatives Were Not Chosen\n\nIgnoring or silently overwriting information breaks traceability.\n\n### Risk\n\nExisting generated documents may still contain outdated wording that needs human review.\n\n### Revisit When\n\nFurther confirmed information changes scope, stack, audience, or production constraints.\n`;
24
31
  if (existsSync(decisionPath))
25
32
  appendFileSync(decisionPath, decision, "utf8");
26
33
  else
27
34
  writeFileSync(decisionPath, `# DECISION_LOG.md${decision}`, "utf8");
28
- const assumptionsPath = join(root, "ASSUMPTIONS.md");
35
+ const assumptionsPath = join(root, ".gaslighting/ASSUMPTIONS.md");
29
36
  if (existsSync(assumptionsPath)) {
30
37
  const current = readFileSync(assumptionsPath, "utf8");
31
38
  writeFileSync(assumptionsPath, current.replaceAll("Status: Active", "Status: Active\n\nOutdated if contradicted by the latest update"), "utf8");
32
39
  }
33
40
  printBanner("UPDATE COMPLETE");
34
- console.log("Updated documents with new information and appended DECISION_LOG.md.");
41
+ console.log("Updated documents with new information and appended .gaslighting/DECISION_LOG.md.");
35
42
  }
@@ -1,8 +1,10 @@
1
1
  import { analyze } from "./analyze.js";
2
2
  import { generateGaslightingMarkdown } from "./generateGaslightingMarkdown.js";
3
3
  import { generatePrdMarkdown } from "./generatePrdMarkdown.js";
4
+ import { generateProjectCareMarkdown } from "./projectCare.js";
4
5
  import { generateAgentsMarkdown, generateAssumptionsMarkdown, generateCodexPromptMarkdown, generateCodexSlashPromptMarkdown, generateDecisionLogMarkdown, generateMemoryMarkdown, generateMissingInfoMarkdown, generateSkillMarkdown, generateSkillOpenAiYaml, generateSkillReferenceDocs, generateStackPolicyMarkdown, } from "./generateOtherMarkdown.js";
5
- export function generateDocs(input) {
6
+ const disciplineDir = ".gaslighting";
7
+ export function generateDocs(input, root = process.cwd()) {
6
8
  const normalized = {
7
9
  ...input,
8
10
  mode: input.mode ?? "standard",
@@ -10,15 +12,17 @@ export function generateDocs(input) {
10
12
  };
11
13
  const analysis = analyze(normalized);
12
14
  const docs = [
13
- { filename: "GASLIGHTING.md", content: generateGaslightingMarkdown(normalized, analysis) },
14
- { filename: "PRD.md", content: generatePrdMarkdown(normalized, analysis) },
15
- { filename: "ASSUMPTIONS.md", content: generateAssumptionsMarkdown(analysis) },
16
- { filename: "MISSING_INFO.md", content: generateMissingInfoMarkdown(analysis) },
17
- { filename: "DECISION_LOG.md", content: generateDecisionLogMarkdown(normalized, analysis) },
18
- { filename: "MEMORY.md", content: generateMemoryMarkdown(normalized, analysis) },
19
- { filename: "STACK_POLICY.md", content: generateStackPolicyMarkdown(analysis) },
20
- { filename: "AGENTS.md", content: generateAgentsMarkdown() },
21
- { filename: "CODEX_PROMPT.md", content: generateCodexPromptMarkdown() },
15
+ { filename: `${disciplineDir}/GASLIGHTING.md`, content: generateGaslightingMarkdown(normalized, analysis) },
16
+ { filename: `${disciplineDir}/PRD.md`, content: generatePrdMarkdown(normalized, analysis) },
17
+ { filename: `${disciplineDir}/ASSUMPTIONS.md`, content: generateAssumptionsMarkdown(analysis) },
18
+ { filename: `${disciplineDir}/MISSING_INFO.md`, content: generateMissingInfoMarkdown(analysis) },
19
+ { filename: `${disciplineDir}/DECISION_LOG.md`, content: generateDecisionLogMarkdown(normalized, analysis) },
20
+ { filename: `${disciplineDir}/MEMORY.md`, content: generateMemoryMarkdown(normalized, analysis) },
21
+ { filename: `${disciplineDir}/STACK_POLICY.md`, content: generateStackPolicyMarkdown(analysis) },
22
+ { filename: `${disciplineDir}/PROJECT_CARE.md`, content: generateProjectCareMarkdown(normalized, analysis, root) },
23
+ { filename: `${disciplineDir}/AGENTS.md`, content: generateAgentsMarkdown() },
24
+ { filename: `${disciplineDir}/CODEX_PROMPT.md`, content: generateCodexPromptMarkdown() },
25
+ { filename: "AGENTS.md", content: generateRootAgentsMarkdown() },
22
26
  { filename: ".codex/prompts/gaslighting.md", content: generateCodexSlashPromptMarkdown() },
23
27
  { filename: ".codex/skills/gaslighting/SKILL.md", content: generateSkillMarkdown() },
24
28
  { filename: ".codex/skills/gaslighting/agents/openai.yaml", content: generateSkillOpenAiYaml() },
@@ -50,9 +54,47 @@ export function generateCodexInstallDocs() {
50
54
  ...skillDocs,
51
55
  { filename: ".codex/prompts/gaslighting.md", content: generateCodexSlashPromptMarkdown() },
52
56
  { filename: ".agents/prompts/gaslighting.md", content: generateCodexSlashPromptMarkdown() },
53
- { filename: "CODEX_GASLIGHTING.md", content: generateCodexPromptMarkdown() },
57
+ { filename: ".gaslighting/CODEX_GASLIGHTING.md", content: generateCodexPromptMarkdown() },
54
58
  ];
55
59
  }
56
60
  export function generateOnlyAgentsDoc() {
57
- return [{ filename: "AGENTS.md", content: generateAgentsMarkdown() }];
61
+ return [
62
+ { filename: `${disciplineDir}/AGENTS.md`, content: generateAgentsMarkdown() },
63
+ { filename: "AGENTS.md", content: generateRootAgentsMarkdown() },
64
+ ];
65
+ }
66
+ export function generateProjectCareDoc(input, root = process.cwd()) {
67
+ const normalized = {
68
+ ...input,
69
+ mode: input.mode ?? "standard",
70
+ language: input.language ?? "en",
71
+ };
72
+ const analysis = analyze(normalized);
73
+ return [{ filename: `${disciplineDir}/PROJECT_CARE.md`, content: generateProjectCareMarkdown(normalized, analysis, root) }];
74
+ }
75
+ function generateRootAgentsMarkdown() {
76
+ return `# AGENTS.md
77
+
78
+ This repository uses Gaslighting-engine.
79
+
80
+ The main discipline documents live under \`.gaslighting/\` to avoid cluttering the project root.
81
+
82
+ Before doing any work, read these files in order:
83
+
84
+ 1. \`.gaslighting/GASLIGHTING.md\`
85
+ 2. \`.gaslighting/PRD.md\`
86
+ 3. \`.gaslighting/STACK_POLICY.md\`
87
+ 4. \`.gaslighting/MISSING_INFO.md\`
88
+ 5. \`.gaslighting/ASSUMPTIONS.md\`
89
+ 6. \`.gaslighting/DECISION_LOG.md\`
90
+ 7. \`.gaslighting/MEMORY.md\`
91
+ 8. \`.gaslighting/PROJECT_CARE.md\`
92
+ 9. \`.gaslighting/AGENTS.md\`
93
+
94
+ Do not reduce scope.
95
+
96
+ Do not fake completion.
97
+
98
+ If care risks are missing or unresolved, record them in \`.gaslighting/PROJECT_CARE.md\` and continue unless they truly block implementation.
99
+ `;
58
100
  }
@@ -216,13 +216,14 @@ This repository uses Gaslighting-engine.
216
216
 
217
217
  Before doing any work, read these files in order:
218
218
 
219
- 1. \`GASLIGHTING.md\`
220
- 2. \`PRD.md\`
221
- 3. \`STACK_POLICY.md\`
222
- 4. \`MISSING_INFO.md\`
223
- 5. \`ASSUMPTIONS.md\`
224
- 6. \`DECISION_LOG.md\`
225
- 7. \`MEMORY.md\`
219
+ 1. \`.gaslighting/GASLIGHTING.md\`
220
+ 2. \`.gaslighting/PRD.md\`
221
+ 3. \`.gaslighting/STACK_POLICY.md\`
222
+ 4. \`.gaslighting/MISSING_INFO.md\`
223
+ 5. \`.gaslighting/ASSUMPTIONS.md\`
224
+ 6. \`.gaslighting/DECISION_LOG.md\`
225
+ 7. \`.gaslighting/MEMORY.md\`
226
+ 8. \`.gaslighting/PROJECT_CARE.md\`
226
227
 
227
228
  ## Prime Directive
228
229
 
@@ -251,8 +252,9 @@ A task is complete only when:
251
252
  - The output can actually be used.
252
253
  - New assumptions are documented.
253
254
  - New technical decisions are recorded.
254
- - Stable project facts are recorded in \`MEMORY.md\`.
255
- - The result does not contradict \`GASLIGHTING.md\`.
255
+ - Stable project facts are recorded in \`.gaslighting/MEMORY.md\`.
256
+ - Operational risks are recorded in \`.gaslighting/PROJECT_CARE.md\`.
257
+ - The result does not contradict \`.gaslighting/GASLIGHTING.md\`.
256
258
 
257
259
  If the task is incomplete, say it is incomplete.
258
260
 
@@ -268,7 +270,7 @@ It is inspired by agent memory systems, but it is not a vague diary.
268
270
 
269
271
  It records stable project facts, recurring workflow rules, known failure patterns, and decisions that future AI-agent sessions must not forget.
270
272
 
271
- Keep required rules in \`AGENTS.md\`, \`GASLIGHTING.md\`, and checked-in documents. Treat this file as a compact recall layer, not the only source of truth.
273
+ Keep required rules in root \`AGENTS.md\`, \`.gaslighting/GASLIGHTING.md\`, and checked-in documents. Treat this file as a compact recall layer, not the only source of truth.
272
274
 
273
275
  ## Stable Project Facts
274
276
 
@@ -305,7 +307,7 @@ These are cleanup and verification rules:
305
307
  - Zero unexplained scope reductions.
306
308
  - Zero unverified success claims.
307
309
  - Every destructive cleanup must be intentional and explainable.
308
- - If code or files are removed, record why in \`DECISION_LOG.md\`.
310
+ - If code or files are removed, record why in \`.gaslighting/DECISION_LOG.md\`.
309
311
 
310
312
  ## Memory Write Rules
311
313
 
@@ -323,7 +325,7 @@ ${bullets(analysis.assumptions)}
323
325
  export function generateSkillMarkdown() {
324
326
  return `---
325
327
  name: gaslighting
326
- description: Generate or update strict project discipline documents for Codex and AI coding agents. Use when a user asks to initialize Gaslighting, create GASLIGHTING.md/PRD.md/AGENTS.md discipline files, preserve full project scope, prevent TODO escape, prevent fake completion, or turn vague project intent into actionable control documents.
328
+ description: Generate or update strict .gaslighting project discipline documents for Codex and AI coding agents. Use when a user asks to initialize Gaslighting, create GASLIGHTING.md/PRD.md/AGENTS.md discipline files, preserve full project scope, prevent TODO escape, prevent fake completion, track Git/GitHub/domain/deployment care risks, or turn vague project intent into actionable control documents.
327
329
  ---
328
330
 
329
331
  # Gaslighting-engine Skill
@@ -339,13 +341,14 @@ Instead:
339
341
  3. Make reasonable assumptions.
340
342
  4. Document assumptions clearly.
341
343
  5. Mark missing information clearly.
342
- 6. Generate \`GASLIGHTING.md\`.
343
- 7. Generate \`PRD.md\`.
344
- 8. Generate \`ASSUMPTIONS.md\`.
345
- 9. Generate \`MISSING_INFO.md\`.
346
- 10. Generate \`DECISION_LOG.md\`.
347
- 11. Generate \`STACK_POLICY.md\`.
348
- 12. Generate or update \`AGENTS.md\`.
344
+ 6. Generate \`.gaslighting/GASLIGHTING.md\`.
345
+ 7. Generate \`.gaslighting/PRD.md\`.
346
+ 8. Generate \`.gaslighting/ASSUMPTIONS.md\`.
347
+ 9. Generate \`.gaslighting/MISSING_INFO.md\`.
348
+ 10. Generate \`.gaslighting/DECISION_LOG.md\`.
349
+ 11. Generate \`.gaslighting/STACK_POLICY.md\`.
350
+ 12. Generate or update root \`AGENTS.md\`.
351
+ 13. Generate \`.gaslighting/PROJECT_CARE.md\`.
349
352
 
350
353
  ## Hardcore Discipline Rule
351
354
 
@@ -410,6 +413,10 @@ Avoid over-engineering.
410
413
 
411
414
  Create real markdown files.
412
415
 
416
+ Keep main project-control files in \`.gaslighting/\` and keep only the Codex pointer \`AGENTS.md\` in the repository root.
417
+
418
+ Use \`.gaslighting/PROJECT_CARE.md\` to track Git/GitHub/domain/deployment/launch risks without blocking implementation.
419
+
413
420
  Do not only explain.
414
421
 
415
422
  Do not produce a plan without files.
@@ -439,14 +446,15 @@ Copy and paste this into Codex:
439
446
 
440
447
  Read the following files before doing any work:
441
448
 
442
- 1. \`GASLIGHTING.md\`
443
- 2. \`PRD.md\`
444
- 3. \`STACK_POLICY.md\`
445
- 4. \`MISSING_INFO.md\`
446
- 5. \`ASSUMPTIONS.md\`
447
- 6. \`DECISION_LOG.md\`
449
+ 1. \`.gaslighting/GASLIGHTING.md\`
450
+ 2. \`.gaslighting/PRD.md\`
451
+ 3. \`.gaslighting/STACK_POLICY.md\`
452
+ 4. \`.gaslighting/MISSING_INFO.md\`
453
+ 5. \`.gaslighting/ASSUMPTIONS.md\`
454
+ 6. \`.gaslighting/DECISION_LOG.md\`
448
455
  7. \`AGENTS.md\`
449
- 8. \`MEMORY.md\`
456
+ 8. \`.gaslighting/MEMORY.md\`
457
+ 9. \`.gaslighting/PROJECT_CARE.md\`
450
458
 
451
459
  Then implement the project MVP.
452
460
 
@@ -462,7 +470,7 @@ Rules:
462
470
  - Do not forget the project purpose.
463
471
  - If something is incomplete, declare it explicitly.
464
472
 
465
- Before claiming completion, perform the self-audit in \`GASLIGHTING.md\`.
473
+ Before claiming completion, perform the self-audit in \`.gaslighting/GASLIGHTING.md\`.
466
474
 
467
475
  Proceed.
468
476
  `;
@@ -472,14 +480,15 @@ export function generateCodexSlashPromptMarkdown() {
472
480
 
473
481
  Read the Gaslighting-engine project-control files before doing any work:
474
482
 
475
- 1. \`GASLIGHTING.md\`
476
- 2. \`PRD.md\`
477
- 3. \`STACK_POLICY.md\`
478
- 4. \`MISSING_INFO.md\`
479
- 5. \`ASSUMPTIONS.md\`
480
- 6. \`DECISION_LOG.md\`
483
+ 1. \`.gaslighting/GASLIGHTING.md\`
484
+ 2. \`.gaslighting/PRD.md\`
485
+ 3. \`.gaslighting/STACK_POLICY.md\`
486
+ 4. \`.gaslighting/MISSING_INFO.md\`
487
+ 5. \`.gaslighting/ASSUMPTIONS.md\`
488
+ 6. \`.gaslighting/DECISION_LOG.md\`
481
489
  7. \`AGENTS.md\`
482
- 8. \`MEMORY.md\`
490
+ 8. \`.gaslighting/MEMORY.md\`
491
+ 9. \`.gaslighting/PROJECT_CARE.md\`
483
492
 
484
493
  Then execute the user's requested implementation.
485
494
 
@@ -493,11 +502,12 @@ Rules:
493
502
  - Do not call scaffolding completion.
494
503
  - Do not call placeholders implementation.
495
504
  - Do not over-engineer.
496
- - Record stable project facts in \`MEMORY.md\`.
497
- - Record product and technical decisions in \`DECISION_LOG.md\`.
505
+ - Record stable project facts in \`.gaslighting/MEMORY.md\`.
506
+ - Record product and technical decisions in \`.gaslighting/DECISION_LOG.md\`.
507
+ - Record Git/GitHub/domain/deployment risks in \`.gaslighting/PROJECT_CARE.md\`.
498
508
  - If something is incomplete, declare it explicitly.
499
509
 
500
- Before claiming completion, perform the self-audit in \`GASLIGHTING.md\`.
510
+ Before claiming completion, perform the self-audit in \`.gaslighting/GASLIGHTING.md\`.
501
511
  `;
502
512
  }
503
513
  export function generateSkillReferenceDocs() {
@@ -0,0 +1,135 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { decisionDate, projectPurpose } from "./content.js";
5
+ export function inspectProjectCare(root) {
6
+ const isGitRepository = runGit(root, ["rev-parse", "--is-inside-work-tree"]) === "true";
7
+ const gitBranch = isGitRepository ? runGit(root, ["branch", "--show-current"]) : undefined;
8
+ const gitRemoteOrigin = isGitRepository ? runGit(root, ["remote", "get-url", "origin"]) : undefined;
9
+ const hasGitHubRemote = Boolean(gitRemoteOrigin && /github\.com[:/]/i.test(gitRemoteOrigin));
10
+ const hasPackageJson = existsSync(join(root, "package.json"));
11
+ const hasVercelConfig = existsSync(join(root, "vercel.json"));
12
+ const hasEnvExample = existsSync(join(root, ".env.example"));
13
+ const checks = [
14
+ {
15
+ label: "Git repository",
16
+ ok: isGitRepository,
17
+ status: isGitRepository ? "pass" : "warn",
18
+ message: isGitRepository ? `Git repository detected${gitBranch ? ` on branch ${gitBranch}` : ""}.` : "Git repository is not initialized.",
19
+ action: isGitRepository ? "Keep committing discipline and implementation changes." : "Ask the user for a GitHub repository URL, then run git init, git remote add origin <url>, commit, and push.",
20
+ },
21
+ {
22
+ label: "GitHub remote",
23
+ ok: hasGitHubRemote,
24
+ status: hasGitHubRemote ? "pass" : "warn",
25
+ message: hasGitHubRemote ? `GitHub remote detected: ${gitRemoteOrigin}` : gitRemoteOrigin ? `Remote exists but is not GitHub: ${gitRemoteOrigin}` : "No origin remote is configured.",
26
+ action: hasGitHubRemote ? "Record repository decisions in DECISION_LOG.md when remote ownership changes." : "Request the GitHub URL or recommend connecting GitHub before production work continues.",
27
+ },
28
+ {
29
+ label: "Project package file",
30
+ ok: hasPackageJson,
31
+ status: hasPackageJson ? "pass" : "warn",
32
+ message: hasPackageJson ? "package.json exists." : "package.json is missing.",
33
+ action: hasPackageJson ? "Keep scripts and package metadata aligned with project purpose." : "Create package.json when implementation starts, unless this is not a Node project.",
34
+ },
35
+ {
36
+ label: "Deployment target",
37
+ ok: hasVercelConfig,
38
+ status: hasVercelConfig ? "pass" : "warn",
39
+ message: hasVercelConfig ? "vercel.json exists." : "No explicit deployment config detected.",
40
+ action: "Confirm deployment target, production domain, preview environment, and rollback path before launch.",
41
+ },
42
+ {
43
+ label: "Environment template",
44
+ ok: hasEnvExample,
45
+ status: hasEnvExample ? "pass" : "warn",
46
+ message: hasEnvExample ? ".env.example exists." : ".env.example is missing.",
47
+ action: "Add .env.example before sharing the project so required secrets are visible without exposing real values.",
48
+ },
49
+ ];
50
+ return {
51
+ isGitRepository,
52
+ gitBranch,
53
+ gitRemoteOrigin,
54
+ hasGitHubRemote,
55
+ hasPackageJson,
56
+ hasVercelConfig,
57
+ hasEnvExample,
58
+ checks,
59
+ };
60
+ }
61
+ export function generateProjectCareMarkdown(input, analysis, root) {
62
+ const care = inspectProjectCare(root);
63
+ const purpose = projectPurpose(analysis.projectType);
64
+ return `# PROJECT_CARE.md
65
+
66
+ This file is the project-care ledger for Gaslighting-engine.
67
+
68
+ It is not a blocker wall.
69
+
70
+ It is the place where the agent keeps noticing operational risk, ownership gaps, Git/GitHub status, deployment readiness, domain ownership, and missing production decisions.
71
+
72
+ The agent must treat this project as mission-critical. If something can put delivery, trust, data, launch, SEO, legal compliance, or operations at risk, record it here and keep pushing it forward.
73
+
74
+ ## Project Snapshot
75
+
76
+ - Date: ${decisionDate()}
77
+ - Original request: ${input.rawUserRequest}
78
+ - Project type: ${analysis.projectType}
79
+ - Classification confidence: ${analysis.confidence}
80
+ - Project purpose:
81
+ ${purpose.map((item) => ` - ${item}`).join("\n")}
82
+
83
+ ## Git And GitHub Registry
84
+
85
+ | Item | Status | Next Action |
86
+ |---|---|---|
87
+ | Git repository | ${care.isGitRepository ? `Detected${care.gitBranch ? `, branch \`${care.gitBranch}\`` : ""}` : "Missing"} | ${care.isGitRepository ? "Commit meaningful checkpoints." : "Ask for GitHub URL, then initialize git and connect origin."} |
88
+ | Origin remote | ${care.gitRemoteOrigin ? `\`${care.gitRemoteOrigin}\`` : "Missing"} | ${care.gitRemoteOrigin ? "Keep remote ownership recorded when it changes." : "Ask for GitHub URL or recommend GitHub connection."} |
89
+ | GitHub remote | ${care.hasGitHubRemote ? "Detected" : "Missing or non-GitHub"} | ${care.hasGitHubRemote ? "Use GitHub as the source-of-truth remote." : "Connect GitHub before production handoff if possible."} |
90
+
91
+ ## Domain And Deployment Registry
92
+
93
+ | Item | Current Status | Risk | Next Action |
94
+ |---|---|---|---|
95
+ | Production domain | Missing | Users cannot reach production reliably. | Confirm domain owner, DNS provider, and final domain. |
96
+ | DNS provider | Missing | Launch can be delayed by DNS access issues. | Record provider, account owner, and nameserver strategy. |
97
+ | Deployment target | ${care.hasVercelConfig ? "Vercel config detected" : "Not confirmed"} | Environment drift and launch confusion. | Confirm Vercel, Netlify, Cloudflare, or other target. |
98
+ | Preview URL policy | Missing | Stakeholders cannot review safely. | Decide preview deployment workflow. |
99
+ | Rollback path | Missing | Production incidents become slower to recover. | Define rollback command/provider workflow. |
100
+
101
+ ## Care Checklist
102
+
103
+ ${care.checks.map((check) => `- [${check.ok ? "x" : " "}] ${check.label}: ${check.message} Next: ${check.action}`).join("\n")}
104
+ - [ ] Production domain confirmed.
105
+ - [ ] DNS owner confirmed.
106
+ - [ ] Deployment provider confirmed.
107
+ - [ ] Analytics ownership confirmed.
108
+ - [ ] Form/email delivery owner confirmed.
109
+ - [ ] Privacy/legal review path confirmed.
110
+ - [ ] Launch checklist created before production release.
111
+
112
+ ## Agent Care Rules
113
+
114
+ - Do not stop implementation just because an operational detail is missing.
115
+ - Do make a practical assumption, record it, and keep moving.
116
+ - If Git is not initialized, ask for a GitHub URL and offer the exact commands.
117
+ - If Git is initialized but no GitHub remote exists, ask for the remote URL and offer \`git remote add origin <url>\`.
118
+ - If GitHub is connected, record the remote URL here and in \`DECISION_LOG.md\` when it changes.
119
+ - If domain or deployment details are missing, keep warning until they are confirmed.
120
+ - Treat missing domain, deployment, analytics, email, and privacy ownership as mission-critical launch risks, not as reasons to abandon the task.
121
+ - Never turn care warnings into fake blockers.
122
+
123
+ ## Current Care Warnings
124
+
125
+ ${care.checks.filter((check) => !check.ok).map((check) => `- ${check.label}: ${check.message} Action: ${check.action}`).join("\n") || "- No care warnings detected right now."}
126
+ `;
127
+ }
128
+ function runGit(root, args) {
129
+ try {
130
+ return execFileSync("git", args, { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || undefined;
131
+ }
132
+ catch {
133
+ return undefined;
134
+ }
135
+ }
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  import { buildCli, normalizeDefaultInitArgv } from "./cli.js";
3
+ import { notifyIfUpdateAvailable } from "./utils/updateCheck.js";
4
+ await notifyIfUpdateAvailable(process.argv);
3
5
  buildCli().parse(normalizeDefaultInitArgv(process.argv));
@@ -1,5 +1,5 @@
1
1
  import { printBanner } from "./banner.js";
2
- export function printSummary(analysis, modeLines, results) {
2
+ export function printSummary(analysis, modeLines, results, care) {
3
3
  printBanner("DOCUMENT GENERATOR");
4
4
  console.log(`Detected project type: ${analysis.projectType}`);
5
5
  console.log(`Confidence: ${analysis.confidence}`);
@@ -22,6 +22,14 @@ export function printSummary(analysis, modeLines, results) {
22
22
  : "";
23
23
  console.log(`- ${result.filename}${suffix}`);
24
24
  }
25
+ if (care) {
26
+ const warnings = care.checks.filter((check) => !check.ok);
27
+ console.log("\nCare:");
28
+ if (warnings.length === 0)
29
+ console.log("- PASS Git/GitHub and local care checks look stable.");
30
+ for (const warning of warnings)
31
+ console.log(`- WARN ${warning.label}: ${warning.message} Next: ${warning.action}`);
32
+ }
25
33
  console.log('\nNext:\nOpen Codex and say:\n');
26
- console.log('"Read GASLIGHTING.md, PRD.md, STACK_POLICY.md, MISSING_INFO.md, ASSUMPTIONS.md, DECISION_LOG.md, MEMORY.md, and AGENTS.md. Implement the MVP without shrinking scope, without TODO escape, and without fake completion."');
34
+ console.log('"Read AGENTS.md, .gaslighting/GASLIGHTING.md, .gaslighting/PRD.md, .gaslighting/STACK_POLICY.md, .gaslighting/MISSING_INFO.md, .gaslighting/ASSUMPTIONS.md, .gaslighting/DECISION_LOG.md, .gaslighting/MEMORY.md, and .gaslighting/PROJECT_CARE.md. Implement the MVP without shrinking scope, without TODO escape, and without fake completion."');
27
35
  }
@@ -0,0 +1,61 @@
1
+ import { packageName, packageVersion } from "../version.js";
2
+ const updateCheckTimeoutMs = 900;
3
+ export async function notifyIfUpdateAvailable(argv) {
4
+ if (shouldSkipUpdateCheck(argv))
5
+ return;
6
+ try {
7
+ const latestVersion = process.env.GASLIGHTING_ENGINE_LATEST_VERSION ?? (await fetchLatestVersion());
8
+ if (!latestVersion || !isNewerVersion(latestVersion, packageVersion))
9
+ return;
10
+ console.error("");
11
+ console.error("Gaslighting-engine update available:");
12
+ console.error(`- current: ${packageVersion}`);
13
+ console.error(`- latest: ${latestVersion}`);
14
+ console.error(`- update: npm install -g ${packageName}@latest`);
15
+ console.error(`- npx: npx ${packageName}@latest`);
16
+ console.error("");
17
+ }
18
+ catch {
19
+ // Update checks must never block the user's actual command.
20
+ }
21
+ }
22
+ async function fetchLatestVersion() {
23
+ const controller = new AbortController();
24
+ const timeout = setTimeout(() => controller.abort(), updateCheckTimeoutMs);
25
+ try {
26
+ const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
27
+ signal: controller.signal,
28
+ headers: { accept: "application/json" },
29
+ });
30
+ if (!response.ok)
31
+ return undefined;
32
+ const data = (await response.json());
33
+ return data.version;
34
+ }
35
+ finally {
36
+ clearTimeout(timeout);
37
+ }
38
+ }
39
+ function shouldSkipUpdateCheck(argv) {
40
+ if (process.env.GASLIGHTING_ENGINE_NO_UPDATE_CHECK === "1")
41
+ return true;
42
+ if (process.env.NO_UPDATE_NOTIFIER === "1")
43
+ return true;
44
+ return argv.some((arg) => ["--help", "-h", "--version", "-V"].includes(arg));
45
+ }
46
+ export function isNewerVersion(candidate, current) {
47
+ const candidateParts = parseVersion(candidate);
48
+ const currentParts = parseVersion(current);
49
+ for (let index = 0; index < 3; index += 1) {
50
+ if (candidateParts[index] > currentParts[index])
51
+ return true;
52
+ if (candidateParts[index] < currentParts[index])
53
+ return false;
54
+ }
55
+ return false;
56
+ }
57
+ function parseVersion(version) {
58
+ const clean = version.split("-")[0] ?? version;
59
+ const parts = clean.split(".").map((part) => Number.parseInt(part, 10));
60
+ return [parts[0] || 0, parts[1] || 0, parts[2] || 0];
61
+ }
@@ -0,0 +1,2 @@
1
+ export const packageName = "gaslighting-engine";
2
+ export const packageVersion = "0.2.0";
@@ -16,7 +16,7 @@ This installs:
16
16
  - `.codex/skills/gaslighting/agents/openai.yaml`
17
17
  - `.agents/prompts/gaslighting.md`
18
18
  - `.codex/prompts/gaslighting.md`
19
- - `CODEX_GASLIGHTING.md`
19
+ - `.gaslighting/CODEX_GASLIGHTING.md`
20
20
 
21
21
  Then use the skill in Codex:
22
22
 
@@ -49,10 +49,10 @@ gaslighting-engine "I want to build a hospital website."
49
49
  Then ask Codex:
50
50
 
51
51
  ```txt
52
- Read GASLIGHTING.md, PRD.md, STACK_POLICY.md, MISSING_INFO.md, ASSUMPTIONS.md, DECISION_LOG.md, MEMORY.md, and AGENTS.md.
52
+ Read AGENTS.md, .gaslighting/GASLIGHTING.md, .gaslighting/PRD.md, .gaslighting/STACK_POLICY.md, .gaslighting/MISSING_INFO.md, .gaslighting/ASSUMPTIONS.md, .gaslighting/DECISION_LOG.md, .gaslighting/MEMORY.md, and .gaslighting/PROJECT_CARE.md.
53
53
  Implement the MVP without shrinking scope, without TODO escape, and without fake completion.
54
54
  ```
55
55
 
56
- Codex should treat `AGENTS.md` as project guidance and `GASLIGHTING.md` as the anti-escape contract.
56
+ Codex should treat root `AGENTS.md` as the project guidance pointer and `.gaslighting/GASLIGHTING.md` as the anti-escape contract.
57
57
 
58
- The CLI also generates Codex Skill files, prompt fallback files, and `CODEX_GASLIGHTING.md` so the workflow still works when a specific Codex surface does not expose a custom slash entry.
58
+ The CLI also generates Codex Skill files, prompt fallback files, `.gaslighting/CODEX_GASLIGHTING.md`, and `.gaslighting/PROJECT_CARE.md` so the workflow still works when a specific Codex surface does not expose a custom slash entry.
package/docs/examples.md CHANGED
@@ -20,3 +20,17 @@ Published usage:
20
20
  ```bash
21
21
  npx gaslighting-engine "I want to build a hospital website."
22
22
  ```
23
+
24
+ This writes discipline documents under `.gaslighting/` and keeps only the Codex pointer `AGENTS.md` in the root.
25
+
26
+ Project-care refresh:
27
+
28
+ ```bash
29
+ npx gaslighting-engine care "I want to build a hospital website." --force
30
+ ```
31
+
32
+ Update check test:
33
+
34
+ ```bash
35
+ GASLIGHTING_ENGINE_LATEST_VERSION=9.9.9 node dist/index.js doctor --path examples/hospital-homepage
36
+ ```