gaslighting-engine 0.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 (89) hide show
  1. package/.codex/prompts/gaslighting.md +30 -0
  2. package/.codex/skills/gaslighting/SKILL.md +96 -0
  3. package/.codex/skills/gaslighting/agents/openai.yaml +8 -0
  4. package/.codex/skills/gaslighting/references/GASLIGHTING_TEMPLATE.md +425 -0
  5. package/.codex/skills/gaslighting/references/HARDCORE_DISCIPLINE_TEMPLATE.md +425 -0
  6. package/.codex/skills/gaslighting/references/HOSPITAL_HOMEPAGE_EXAMPLE.md +119 -0
  7. package/.codex/skills/gaslighting/references/STACK_POLICY_TEMPLATE.md +64 -0
  8. package/.codex/skills/gaslighting/scripts/generate-gaslighting-docs.ts +3 -0
  9. package/LICENSE +21 -0
  10. package/README.md +200 -0
  11. package/dist/cli.js +118 -0
  12. package/dist/commands/agents.js +10 -0
  13. package/dist/commands/codexInstall.js +60 -0
  14. package/dist/commands/doctor.js +42 -0
  15. package/dist/commands/generate.js +4 -0
  16. package/dist/commands/init.js +27 -0
  17. package/dist/commands/skill.js +10 -0
  18. package/dist/commands/update.js +35 -0
  19. package/dist/core/analyze.js +132 -0
  20. package/dist/core/classifyProjectType.js +66 -0
  21. package/dist/core/content.js +125 -0
  22. package/dist/core/detectStackHints.js +34 -0
  23. package/dist/core/generateDocs.js +58 -0
  24. package/dist/core/generateGaslightingMarkdown.js +420 -0
  25. package/dist/core/generateOtherMarkdown.js +529 -0
  26. package/dist/core/generatePrdMarkdown.js +125 -0
  27. package/dist/index.js +3 -0
  28. package/dist/types.js +1 -0
  29. package/dist/utils/banner.js +49 -0
  30. package/dist/utils/date.js +6 -0
  31. package/dist/utils/file.js +24 -0
  32. package/dist/utils/logger.js +27 -0
  33. package/dist/utils/markdown.js +6 -0
  34. package/docs/codex-usage.md +58 -0
  35. package/docs/examples.md +22 -0
  36. package/docs/philosophy.md +17 -0
  37. package/docs/research.md +54 -0
  38. package/examples/ecommerce/.codex/prompts/gaslighting.md +30 -0
  39. package/examples/ecommerce/.codex/skills/gaslighting/SKILL.md +96 -0
  40. package/examples/ecommerce/.codex/skills/gaslighting/agents/openai.yaml +8 -0
  41. package/examples/ecommerce/.codex/skills/gaslighting/references/GASLIGHTING_TEMPLATE.md +425 -0
  42. package/examples/ecommerce/.codex/skills/gaslighting/references/HARDCORE_DISCIPLINE_TEMPLATE.md +425 -0
  43. package/examples/ecommerce/.codex/skills/gaslighting/references/HOSPITAL_HOMEPAGE_EXAMPLE.md +119 -0
  44. package/examples/ecommerce/.codex/skills/gaslighting/references/STACK_POLICY_TEMPLATE.md +64 -0
  45. package/examples/ecommerce/.codex/skills/gaslighting/scripts/generate-gaslighting-docs.ts +3 -0
  46. package/examples/ecommerce/AGENTS.md +47 -0
  47. package/examples/ecommerce/ASSUMPTIONS.md +146 -0
  48. package/examples/ecommerce/CODEX_PROMPT.md +34 -0
  49. package/examples/ecommerce/DECISION_LOG.md +95 -0
  50. package/examples/ecommerce/GASLIGHTING.md +429 -0
  51. package/examples/ecommerce/MEMORY.md +63 -0
  52. package/examples/ecommerce/MISSING_INFO.md +13 -0
  53. package/examples/ecommerce/PRD.md +115 -0
  54. package/examples/ecommerce/STACK_POLICY.md +64 -0
  55. package/examples/hospital-homepage/.codex/prompts/gaslighting.md +30 -0
  56. package/examples/hospital-homepage/.codex/skills/gaslighting/SKILL.md +96 -0
  57. package/examples/hospital-homepage/.codex/skills/gaslighting/agents/openai.yaml +8 -0
  58. package/examples/hospital-homepage/.codex/skills/gaslighting/references/GASLIGHTING_TEMPLATE.md +425 -0
  59. package/examples/hospital-homepage/.codex/skills/gaslighting/references/HARDCORE_DISCIPLINE_TEMPLATE.md +425 -0
  60. package/examples/hospital-homepage/.codex/skills/gaslighting/references/HOSPITAL_HOMEPAGE_EXAMPLE.md +119 -0
  61. package/examples/hospital-homepage/.codex/skills/gaslighting/references/STACK_POLICY_TEMPLATE.md +64 -0
  62. package/examples/hospital-homepage/.codex/skills/gaslighting/scripts/generate-gaslighting-docs.ts +3 -0
  63. package/examples/hospital-homepage/AGENTS.md +47 -0
  64. package/examples/hospital-homepage/ASSUMPTIONS.md +218 -0
  65. package/examples/hospital-homepage/CODEX_PROMPT.md +34 -0
  66. package/examples/hospital-homepage/DECISION_LOG.md +95 -0
  67. package/examples/hospital-homepage/GASLIGHTING.md +432 -0
  68. package/examples/hospital-homepage/MEMORY.md +66 -0
  69. package/examples/hospital-homepage/MISSING_INFO.md +13 -0
  70. package/examples/hospital-homepage/PRD.md +119 -0
  71. package/examples/hospital-homepage/STACK_POLICY.md +64 -0
  72. package/examples/landing-page/.codex/prompts/gaslighting.md +30 -0
  73. package/examples/landing-page/.codex/skills/gaslighting/SKILL.md +96 -0
  74. package/examples/landing-page/.codex/skills/gaslighting/agents/openai.yaml +8 -0
  75. package/examples/landing-page/.codex/skills/gaslighting/references/GASLIGHTING_TEMPLATE.md +425 -0
  76. package/examples/landing-page/.codex/skills/gaslighting/references/HARDCORE_DISCIPLINE_TEMPLATE.md +425 -0
  77. package/examples/landing-page/.codex/skills/gaslighting/references/HOSPITAL_HOMEPAGE_EXAMPLE.md +119 -0
  78. package/examples/landing-page/.codex/skills/gaslighting/references/STACK_POLICY_TEMPLATE.md +64 -0
  79. package/examples/landing-page/.codex/skills/gaslighting/scripts/generate-gaslighting-docs.ts +3 -0
  80. package/examples/landing-page/AGENTS.md +47 -0
  81. package/examples/landing-page/ASSUMPTIONS.md +146 -0
  82. package/examples/landing-page/CODEX_PROMPT.md +34 -0
  83. package/examples/landing-page/DECISION_LOG.md +95 -0
  84. package/examples/landing-page/GASLIGHTING.md +424 -0
  85. package/examples/landing-page/MEMORY.md +63 -0
  86. package/examples/landing-page/MISSING_INFO.md +13 -0
  87. package/examples/landing-page/PRD.md +103 -0
  88. package/examples/landing-page/STACK_POLICY.md +64 -0
  89. package/package.json +37 -0
package/README.md ADDED
@@ -0,0 +1,200 @@
1
+ # Gaslighting-engine
2
+
3
+ ```txt
4
+ _ _ _ ____ ____ ___
5
+ | | | | | || _ \ / ___|_ _|
6
+ | | | | | || | | | | _ | |
7
+ | |___ | |_| || |_| | |_| || |
8
+ |_____| \___/ |____/ \____|___|
9
+ ```
10
+
11
+ AI coding agents love to escape.
12
+
13
+ They start strong, then drift.
14
+
15
+ They implement three examples and call it done.
16
+
17
+ They leave TODOs.
18
+
19
+ They create placeholders.
20
+
21
+ They say "the rest follows the same pattern."
22
+
23
+ Gaslighting-engine is a strict project-discipline generator that stops this behavior.
24
+
25
+ It generates:
26
+
27
+ - `GASLIGHTING.md`
28
+ - `PRD.md`
29
+ - `ASSUMPTIONS.md`
30
+ - `MISSING_INFO.md`
31
+ - `DECISION_LOG.md`
32
+ - `MEMORY.md`
33
+ - `STACK_POLICY.md`
34
+ - `AGENTS.md`
35
+ - `CODEX_PROMPT.md`
36
+ - `.codex/prompts/gaslighting.md`
37
+ - Codex Skill files
38
+
39
+ ## Quick Start
40
+
41
+ ```bash
42
+ npx gaslighting-engine "I want to build a hospital website."
43
+ ```
44
+
45
+ Short aliases after global install or local link:
46
+
47
+ ```bash
48
+ gaslighting-engine "I want to build a hospital website."
49
+ gaslighting "I want to build a hospital website."
50
+ ```
51
+
52
+ The default mode is already aggressive:
53
+
54
+ - hardcore discipline
55
+ - full-scope enforcement
56
+ - no-TODO escape prevention
57
+ - no-shortcut enforcement
58
+ - Codex Skill generation
59
+ - Codex prompt fallback generation
60
+
61
+ ## What It Does
62
+
63
+ It turns vague intent into strict AI-readable project discipline.
64
+
65
+ It tells the agent:
66
+
67
+ - what the project is
68
+ - what the project must not become
69
+ - what assumptions are being made
70
+ - what information is missing
71
+ - what stack to use
72
+ - what not to over-engineer
73
+ - what fake completion looks like
74
+ - what counts as done
75
+ - what stable project facts should be remembered
76
+
77
+ ## Commands
78
+
79
+ ```bash
80
+ gaslighting "I want to build a hospital website."
81
+ gaslighting-engine "I want to build a hospital website."
82
+ gaslighting init "I want to build a hospital website."
83
+ gaslighting-engine generate "Build an ecommerce MVP."
84
+ gaslighting-engine update "The hospital is actually an OB-GYN clinic, not dermatology."
85
+ gaslighting-engine doctor
86
+ gaslighting-engine codex-install
87
+ gaslighting-engine codex-doctor
88
+ gaslighting-engine skill
89
+ gaslighting-engine agents
90
+ ```
91
+
92
+ ## Useful Options
93
+
94
+ Options are escape hatches. The strict behavior is already the default.
95
+
96
+ ```bash
97
+ --standard
98
+ --allow-partial
99
+ --allow-todo
100
+ --allow-shortcut
101
+ --force
102
+ --dry-run
103
+ --lang en
104
+ --lang ko
105
+ --type hospital_homepage
106
+ --type ecommerce
107
+ --type landing_page
108
+ --type admin_dashboard
109
+ ```
110
+
111
+ ## Why the Name?
112
+
113
+ Because AI agents often need aggressive reminders.
114
+
115
+ This project does not manipulate humans.
116
+
117
+ It disciplines AI coding agents.
118
+
119
+ ## Core Rule
120
+
121
+ Fake completion is worse than honest incompletion.
122
+
123
+ If the work is not done, say it is not done.
124
+
125
+ ## Use with Codex
126
+
127
+ For the smoothest Codex app setup, run this in the project:
128
+
129
+ ```bash
130
+ npx gaslighting-engine codex-install --force
131
+ ```
132
+
133
+ This writes both current Codex Skill paths used by different Codex surfaces:
134
+
135
+ ```txt
136
+ .agents/skills/gaslighting/SKILL.md
137
+ .agents/skills/gaslighting/agents/openai.yaml
138
+ .codex/skills/gaslighting/SKILL.md
139
+ .codex/skills/gaslighting/agents/openai.yaml
140
+ .agents/prompts/gaslighting.md
141
+ .codex/prompts/gaslighting.md
142
+ CODEX_GASLIGHTING.md
143
+ ```
144
+
145
+ Then use one of these in Codex:
146
+
147
+ ```txt
148
+ $gaslighting
149
+ ```
150
+
151
+ or:
152
+
153
+ ```txt
154
+ Use the gaslighting skill.
155
+ ```
156
+
157
+ In Codex app builds that expose enabled skills in the slash menu, `/gaslighting` may also appear. If it does not appear, `$gaslighting` is the reliable direct invocation path.
158
+
159
+ After generating project documents, tell Codex:
160
+
161
+ ```txt
162
+ Read GASLIGHTING.md, PRD.md, STACK_POLICY.md, MISSING_INFO.md, ASSUMPTIONS.md, DECISION_LOG.md, MEMORY.md, and AGENTS.md.
163
+ Do not shrink the scope.
164
+ Do not leave TODOs.
165
+ Do not fake completion.
166
+ ```
167
+
168
+ `AGENTS.md` remains the project-level safety net. Codex reads it as project guidance, while the skill gives you an explicit reusable workflow.
169
+
170
+ ## Use as Codex Skill
171
+
172
+ Generate skill files:
173
+
174
+ ```bash
175
+ gaslighting-engine skill
176
+ ```
177
+
178
+ This creates:
179
+
180
+ ```txt
181
+ .codex/prompts/gaslighting.md
182
+ .codex/skills/gaslighting/SKILL.md
183
+ .codex/skills/gaslighting/agents/openai.yaml
184
+ .codex/skills/gaslighting/references/GASLIGHTING_TEMPLATE.md
185
+ .codex/skills/gaslighting/references/HARDCORE_DISCIPLINE_TEMPLATE.md
186
+ .codex/skills/gaslighting/references/STACK_POLICY_TEMPLATE.md
187
+ .codex/skills/gaslighting/references/HOSPITAL_HOMEPAGE_EXAMPLE.md
188
+ .codex/skills/gaslighting/scripts/generate-gaslighting-docs.ts
189
+ ```
190
+
191
+ ## Development
192
+
193
+ ```bash
194
+ npm install
195
+ npm run build
196
+ node dist/index.js "I want to build a hospital website." --dry-run
197
+ npm test
198
+ ```
199
+
200
+ The MVP is deterministic and template-based. It does not call OpenAI, Anthropic, or any external LLM API.
package/dist/cli.js ADDED
@@ -0,0 +1,118 @@
1
+ import { Command, InvalidArgumentError } from "commander";
2
+ import { runAgents } from "./commands/agents.js";
3
+ import { runCodexDoctor, runCodexInstall } from "./commands/codexInstall.js";
4
+ import { runDoctor } from "./commands/doctor.js";
5
+ import { runGenerate } from "./commands/generate.js";
6
+ import { runInit } from "./commands/init.js";
7
+ import { runSkill } from "./commands/skill.js";
8
+ import { runUpdate } from "./commands/update.js";
9
+ const projectTypes = [
10
+ "hospital_homepage",
11
+ "ecommerce",
12
+ "landing_page",
13
+ "admin_dashboard",
14
+ "saas_mvp",
15
+ "portfolio_site",
16
+ "blog_seo_site",
17
+ "reservation_site",
18
+ "outsourcing_platform",
19
+ "public_bid_proposal",
20
+ "legal_consulting_site",
21
+ "education_site",
22
+ "generic_web_project",
23
+ ];
24
+ function parseProjectType(value) {
25
+ if (projectTypes.includes(value))
26
+ return value;
27
+ throw new InvalidArgumentError(`Invalid project type. Use one of: ${projectTypes.join(", ")}`);
28
+ }
29
+ function addCommonOptions(command) {
30
+ return command
31
+ .option("--hardcore", "generate aggressive anti-escape discipline (default)")
32
+ .option("--standard", "downgrade generated discipline from hardcore to standard")
33
+ .option("--strict", "use strict defaults (default)")
34
+ .option("--full-scope", "force scope-preservation rules (default)")
35
+ .option("--allow-partial", "allow partial-scope mode for unusual cases")
36
+ .option("--no-todo", "forbid TODO-based fake completion (default)")
37
+ .option("--allow-todo", "allow honest TODOs in generated guidance")
38
+ .option("--no-shortcut", "forbid representative examples and summary escape (default)")
39
+ .option("--allow-shortcut", "allow representative examples in generated guidance")
40
+ .option("--force", "overwrite existing files")
41
+ .option("--dry-run", "print files without writing")
42
+ .option("--lang <lang>", "language: en or ko", "en")
43
+ .option("--type <type>", "project type override", parseProjectType)
44
+ .option("--path <path>", "target directory");
45
+ }
46
+ export function buildCli() {
47
+ const program = new Command();
48
+ program
49
+ .name("gaslighting-engine")
50
+ .description("LUDGI Gaslighting-engine: a hardcore project-discipline generator for AI coding agents.")
51
+ .version("0.1.0")
52
+ .showHelpAfterError()
53
+ .addHelpText("after", `
54
+ Examples:
55
+ $ gaslighting-engine "I want to build a hospital website."
56
+ $ gaslighting "Build an ecommerce MVP." --type ecommerce
57
+ $ gaslighting-engine init "Build a landing page" --dry-run
58
+ $ gaslighting-engine doctor
59
+ $ gaslighting-engine codex-install --force
60
+
61
+ Defaults:
62
+ hardcore, full-scope, no-TODO, and no-shortcut are enabled by default.
63
+ `);
64
+ addCommonOptions(program.command("init <request>").description("Initialize Gaslighting documents")).action(runInit);
65
+ addCommonOptions(program.command("generate <request>").description("Generate documents from input without interactive setup")).action(runGenerate);
66
+ program
67
+ .command("update <information>")
68
+ .description("Append new information to existing discipline documents")
69
+ .option("--dry-run", "print files without writing")
70
+ .option("--path <path>", "target directory")
71
+ .action(runUpdate);
72
+ program
73
+ .command("doctor")
74
+ .description("Check whether the repository is properly disciplined")
75
+ .option("--path <path>", "target directory")
76
+ .action(runDoctor);
77
+ program
78
+ .command("skill")
79
+ .description("Generate only Codex Skill files")
80
+ .option("--force", "overwrite existing files")
81
+ .option("--dry-run", "print files without writing")
82
+ .option("--path <path>", "target directory")
83
+ .action(runSkill);
84
+ program
85
+ .command("agents")
86
+ .description("Generate or update AGENTS.md")
87
+ .option("--force", "overwrite existing files")
88
+ .option("--dry-run", "print files without writing")
89
+ .option("--path <path>", "target directory")
90
+ .action(runAgents);
91
+ program
92
+ .command("codex-install")
93
+ .description("Install Gaslighting as a Codex-optimized project or user skill")
94
+ .option("--force", "overwrite existing files")
95
+ .option("--dry-run", "print files without writing")
96
+ .option("--path <path>", "target directory")
97
+ .option("--scope <scope>", "project or user", "project")
98
+ .action(runCodexInstall);
99
+ program
100
+ .command("codex-doctor")
101
+ .description("Check Codex-optimized Gaslighting install files")
102
+ .option("--path <path>", "target directory")
103
+ .option("--scope <scope>", "project or user", "project")
104
+ .action(runCodexDoctor);
105
+ return program;
106
+ }
107
+ export function normalizeDefaultInitArgv(argv) {
108
+ const args = argv.slice(2);
109
+ if (args.length === 0)
110
+ return argv;
111
+ if (args.some((arg) => ["--help", "-h", "--version", "-V"].includes(arg)))
112
+ return argv;
113
+ const commands = new Set(["init", "generate", "update", "doctor", "skill", "agents", "codex-install", "codex-doctor"]);
114
+ const firstMeaningful = args.find((arg) => !arg.startsWith("-"));
115
+ if (firstMeaningful && commands.has(firstMeaningful))
116
+ return argv;
117
+ return [argv[0] ?? "node", argv[1] ?? "gaslighting-engine", "init", ...args];
118
+ }
@@ -0,0 +1,10 @@
1
+ import { generateOnlyAgentsDoc } from "../core/generateDocs.js";
2
+ import { printBanner } from "../utils/banner.js";
3
+ import { writeDocs } from "../utils/file.js";
4
+ export function runAgents(options) {
5
+ const results = writeDocs(options.path ?? process.cwd(), generateOnlyAgentsDoc(), options.force, options.dryRun);
6
+ printBanner("AGENTS INSTALL");
7
+ console.log("Created:");
8
+ for (const result of results)
9
+ console.log(`- ${result.filename}${result.status === "dry_run" ? " (dry run)" : ""}`);
10
+ }
@@ -0,0 +1,60 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { generateCodexInstallDocs } from "../core/generateDocs.js";
4
+ import { printBanner } from "../utils/banner.js";
5
+ import { writeDocs } from "../utils/file.js";
6
+ export function runCodexInstall(options) {
7
+ const target = resolveTargetPath(options);
8
+ const results = writeDocs(target, generateCodexInstallDocs(), options.force, options.dryRun);
9
+ printBanner("CODEX INSTALL");
10
+ console.log(`Scope: ${options.scope ?? "project"}`);
11
+ console.log(`Target: ${target}\n`);
12
+ console.log("Installed:");
13
+ for (const result of results) {
14
+ const suffix = result.status === "created_variant"
15
+ ? " (existing file preserved)"
16
+ : result.status === "overwritten"
17
+ ? " (overwritten)"
18
+ : result.status === "dry_run"
19
+ ? " (dry run)"
20
+ : "";
21
+ console.log(`- ${result.filename}${suffix}`);
22
+ }
23
+ console.log("\nCodex usage:");
24
+ console.log('- Direct invocation: type "$gaslighting" in the Codex composer.');
25
+ console.log('- Natural invocation: say "Use the gaslighting skill."');
26
+ console.log('- If Codex exposes enabled skills in slash commands, use: /gaslighting');
27
+ console.log("- If neither appears immediately, restart Codex and reopen this project.");
28
+ console.log("- Fallback prompt file: CODEX_GASLIGHTING.md");
29
+ }
30
+ export function runCodexDoctor(options) {
31
+ const target = resolveTargetPath(options);
32
+ const checks = [
33
+ ".agents/skills/gaslighting/SKILL.md",
34
+ ".agents/skills/gaslighting/agents/openai.yaml",
35
+ ".codex/skills/gaslighting/SKILL.md",
36
+ ".codex/skills/gaslighting/agents/openai.yaml",
37
+ ".codex/prompts/gaslighting.md",
38
+ ".agents/prompts/gaslighting.md",
39
+ "CODEX_GASLIGHTING.md",
40
+ ].map((file) => ({ file, ok: existsSync(join(target, file)) }));
41
+ printBanner("CODEX DOCTOR");
42
+ console.log(`Scope: ${options.scope ?? "project"}`);
43
+ console.log(`Target: ${target}\n`);
44
+ for (const check of checks)
45
+ console.log(`${check.ok ? "PASS" : "WARN"} ${check.file}`);
46
+ const hasSkill = checks.some((check) => check.ok && check.file.endsWith("skills/gaslighting/SKILL.md"));
47
+ if (!hasSkill) {
48
+ process.exitCode = 1;
49
+ console.log("\nNo gaslighting skill was found. Run: gaslighting codex-install --force");
50
+ return;
51
+ }
52
+ console.log("\nCodex install looks usable. Restart Codex if the skill does not appear yet.");
53
+ }
54
+ function resolveTargetPath(options) {
55
+ if (options.path)
56
+ return resolve(options.path);
57
+ if (options.scope === "user")
58
+ return process.env.USERPROFILE ?? process.env.HOME ?? process.cwd();
59
+ return process.cwd();
60
+ }
@@ -0,0 +1,42 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { printBanner } from "../utils/banner.js";
4
+ export function runDoctor(options) {
5
+ const root = options.path ?? process.cwd();
6
+ const read = (file) => {
7
+ const full = join(root, file);
8
+ return existsSync(full) ? readFileSync(full, "utf8") : "";
9
+ };
10
+ const gaslighting = read("GASLIGHTING.md");
11
+ const agents = read("AGENTS.md");
12
+ const checks = [
13
+ fileCheck(root, "GASLIGHTING.md"),
14
+ fileCheck(root, "PRD.md"),
15
+ 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) },
24
+ fileCheck(root, ".codex/skills/gaslighting/SKILL.md"),
25
+ fileCheck(root, ".codex/skills/gaslighting/agents/openai.yaml"),
26
+ fileCheck(root, ".codex/prompts/gaslighting.md"),
27
+ ];
28
+ printBanner("DOCTOR");
29
+ for (const check of checks)
30
+ console.log(`${check.ok ? "PASS" : "FAIL"} ${check.name}`);
31
+ const failed = checks.filter((check) => !check.ok);
32
+ if (failed.length) {
33
+ console.log(`\n${failed.length} check(s) failed.`);
34
+ process.exitCode = 1;
35
+ }
36
+ else {
37
+ console.log("\nRepository discipline looks intact.");
38
+ }
39
+ }
40
+ function fileCheck(root, file) {
41
+ return { name: `${file} exists`, ok: existsSync(join(root, file)) };
42
+ }
@@ -0,0 +1,4 @@
1
+ import { runInit } from "./init.js";
2
+ export function runGenerate(rawUserRequest, options) {
3
+ runInit(rawUserRequest, options);
4
+ }
@@ -0,0 +1,27 @@
1
+ import { generateDocs } from "../core/generateDocs.js";
2
+ import { modeSummary } from "../core/content.js";
3
+ import { writeDocs } from "../utils/file.js";
4
+ import { printSummary } from "../utils/logger.js";
5
+ export function runInit(rawUserRequest, options) {
6
+ const input = {
7
+ rawUserRequest,
8
+ projectType: options.type,
9
+ language: options.lang ?? "en",
10
+ mode: options.standard ? "standard" : "hardcore",
11
+ fullScope: !options.allowPartial,
12
+ noTodo: !options.allowTodo,
13
+ noShortcut: !options.allowShortcut,
14
+ force: options.force,
15
+ dryRun: options.dryRun,
16
+ };
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);
20
+ if (options.dryRun) {
21
+ console.log("\nPreviews:");
22
+ for (const doc of docs) {
23
+ const preview = doc.content.split("\n").slice(0, 8).join("\n");
24
+ console.log(`\n--- ${doc.filename} ---\n${preview}`);
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,10 @@
1
+ import { generateOnlySkillDocs } from "../core/generateDocs.js";
2
+ import { printBanner } from "../utils/banner.js";
3
+ import { writeDocs } from "../utils/file.js";
4
+ export function runSkill(options) {
5
+ const results = writeDocs(options.path ?? process.cwd(), generateOnlySkillDocs(), options.force, options.dryRun);
6
+ printBanner("CODEX SKILL INSTALL");
7
+ console.log("Created:");
8
+ for (const result of results)
9
+ console.log(`- ${result.filename}${result.status === "dry_run" ? " (dry run)" : ""}`);
10
+ }
@@ -0,0 +1,35 @@
1
+ import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { printBanner } from "../utils/banner.js";
4
+ import { isoDate } from "../utils/date.js";
5
+ export function runUpdate(newInformation, options) {
6
+ const root = options.path ?? process.cwd();
7
+ const files = ["GASLIGHTING.md", "PRD.md", "ASSUMPTIONS.md", "MISSING_INFO.md", "MEMORY.md"];
8
+ 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
+ if (options.dryRun) {
10
+ printBanner("UPDATE DRY RUN");
11
+ console.log("Dry run update would touch:");
12
+ for (const file of files)
13
+ console.log(`- ${file}`);
14
+ console.log("- DECISION_LOG.md");
15
+ return;
16
+ }
17
+ for (const file of files) {
18
+ const path = join(root, file);
19
+ if (existsSync(path))
20
+ appendFileSync(path, block, "utf8");
21
+ }
22
+ const decisionPath = join(root, "DECISION_LOG.md");
23
+ 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
+ if (existsSync(decisionPath))
25
+ appendFileSync(decisionPath, decision, "utf8");
26
+ else
27
+ writeFileSync(decisionPath, `# DECISION_LOG.md${decision}`, "utf8");
28
+ const assumptionsPath = join(root, "ASSUMPTIONS.md");
29
+ if (existsSync(assumptionsPath)) {
30
+ const current = readFileSync(assumptionsPath, "utf8");
31
+ writeFileSync(assumptionsPath, current.replaceAll("Status: Active", "Status: Active\n\nOutdated if contradicted by the latest update"), "utf8");
32
+ }
33
+ printBanner("UPDATE COMPLETE");
34
+ console.log("Updated documents with new information and appended DECISION_LOG.md.");
35
+ }
@@ -0,0 +1,132 @@
1
+ import { classifyProjectType } from "./classifyProjectType.js";
2
+ import { detectStackHints } from "./detectStackHints.js";
3
+ export function analyze(input) {
4
+ const classified = classifyProjectType(input.rawUserRequest, input.projectType);
5
+ const detectedStackHints = detectStackHints(input.rawUserRequest);
6
+ const assumptions = assumptionsFor(classified.projectType, detectedStackHints);
7
+ const missingInfo = missingInfoFor(classified.projectType, classified.confidence);
8
+ return {
9
+ projectType: classified.projectType,
10
+ confidence: classified.confidence,
11
+ detectedStackHints,
12
+ assumptions,
13
+ missingInfo,
14
+ };
15
+ }
16
+ function assumptionsFor(projectType, stackHints) {
17
+ const common = [
18
+ stackHints.length > 0
19
+ ? `Use detected stack hints where practical: ${stackHints.join(", ")}.`
20
+ : "Use the default practical web stack unless the user later specifies otherwise.",
21
+ "Build an MVP that can be implemented locally without a SaaS backend for this generator.",
22
+ "Treat unspecified brand/content details as temporary copy that must be human-reviewed before production.",
23
+ ];
24
+ const byType = {
25
+ hospital_homepage: [
26
+ "Hospital name: Sample Clinic.",
27
+ "Hospital type: dermatology/aesthetic clinic.",
28
+ "Location: Seoul area.",
29
+ "Main goal: trust, consultation inquiries, reservations, local SEO, and mobile contact clarity.",
30
+ "Admin dashboard is excluded from MVP unless requested later.",
31
+ "Medical ad copy requires human review before production.",
32
+ ],
33
+ ecommerce: [
34
+ "Store name: Sample Store.",
35
+ "MVP requires product discovery, cart, checkout preparation, order creation, and admin product/order operations.",
36
+ "Payment provider is a production decision; Stripe or Toss Payments should be selected based on market.",
37
+ ],
38
+ landing_page: [
39
+ "The page has one primary conversion goal.",
40
+ "Offer details are temporary and must be replaced by the owner.",
41
+ "A lead form and repeated CTA path are required.",
42
+ ],
43
+ admin_dashboard: [
44
+ "Dashboard users are internal operators.",
45
+ "Operational data needs search, filtering, safe editing, empty states, and error states.",
46
+ "Role rules are assumed simple until specified.",
47
+ ],
48
+ saas_mvp: ["SaaS MVP needs account-aware workflows, core value loop, billing readiness, and admin visibility."],
49
+ portfolio_site: ["Portfolio goal is credibility, case-study clarity, and contact conversion."],
50
+ blog_seo_site: ["Blog goal is indexed content, categories, metadata, and maintainable publishing flow."],
51
+ reservation_site: ["Reservation flow must cover service selection, time selection, contact details, and confirmation."],
52
+ outsourcing_platform: ["Platform goal is matching demand and supply with inquiry or transaction flow."],
53
+ public_bid_proposal: ["Proposal output must preserve scoring criteria, evidence, compliance, and deliverable traceability."],
54
+ legal_consulting_site: ["Legal site goal is trust, practice-area clarity, consultation conversion, and compliance review."],
55
+ education_site: ["Education site goal is course discovery, enrollment interest, curriculum clarity, and learner trust."],
56
+ generic_web_project: ["Project type is uncertain, so preserve the broad user request and avoid narrowing the purpose silently."],
57
+ };
58
+ return [...byType[projectType], ...common];
59
+ }
60
+ function missingInfoFor(projectType, confidence) {
61
+ const common = [
62
+ {
63
+ item: "Final brand identity and production copy",
64
+ currentStatus: "Missing but non-blocking",
65
+ temporaryAssumption: "Use clear temporary copy and document it as replaceable.",
66
+ impact: "medium",
67
+ blocking: "blocking_before_production",
68
+ recommendedAction: "Collect final brand name, tone, assets, and legally approved copy before launch.",
69
+ },
70
+ {
71
+ item: "Exact deployment credentials and accounts",
72
+ currentStatus: "Missing but non-blocking",
73
+ temporaryAssumption: "Prepare the app for Vercel-style deployment without requiring credentials during implementation.",
74
+ impact: "medium",
75
+ blocking: "blocking_before_production",
76
+ recommendedAction: "Add project-specific env vars and provider accounts before production deployment.",
77
+ },
78
+ ];
79
+ if (confidence === "low") {
80
+ common.unshift({
81
+ item: "Project type",
82
+ currentStatus: "Assumed with low confidence",
83
+ temporaryAssumption: "Treat as generic_web_project until a clearer vertical is provided.",
84
+ impact: "high",
85
+ blocking: "risky_but_workable",
86
+ recommendedAction: "Confirm the project vertical, target audience, and primary conversion goal.",
87
+ });
88
+ }
89
+ const byType = {
90
+ hospital_homepage: [
91
+ {
92
+ item: "Medical department, doctors, and clinic credentials",
93
+ currentStatus: "Missing but non-blocking",
94
+ temporaryAssumption: "Use dermatology/aesthetic clinic structure with Sample Clinic placeholders.",
95
+ impact: "high",
96
+ blocking: "blocking_before_production",
97
+ recommendedAction: "Replace with verified doctors, treatments, licenses, address, phone, and medical copy.",
98
+ },
99
+ ],
100
+ ecommerce: [
101
+ {
102
+ item: "Product catalog, payment market, and fulfillment rules",
103
+ currentStatus: "Missing but non-blocking",
104
+ temporaryAssumption: "Use sample products and a payment-ready checkout architecture.",
105
+ impact: "high",
106
+ blocking: "blocking_before_production",
107
+ recommendedAction: "Provide real SKU data, shipping policy, tax rules, payment provider, and refund policy.",
108
+ },
109
+ ],
110
+ landing_page: [
111
+ {
112
+ item: "Exact offer and conversion goal",
113
+ currentStatus: "Missing but non-blocking",
114
+ temporaryAssumption: "Use one primary lead capture CTA.",
115
+ impact: "high",
116
+ blocking: "risky_but_workable",
117
+ recommendedAction: "Confirm the offer, target audience, ad source, CTA, and lead destination.",
118
+ },
119
+ ],
120
+ admin_dashboard: [
121
+ {
122
+ item: "Real data schema and permission matrix",
123
+ currentStatus: "Missing but non-blocking",
124
+ temporaryAssumption: "Use a practical CRUD-oriented data model and simple operator/admin roles.",
125
+ impact: "high",
126
+ blocking: "risky_but_workable",
127
+ recommendedAction: "Confirm entities, workflows, audit needs, and role permissions.",
128
+ },
129
+ ],
130
+ };
131
+ return [...(byType[projectType] ?? []), ...common];
132
+ }