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.
- package/.codex/prompts/gaslighting.md +30 -0
- package/.codex/skills/gaslighting/SKILL.md +96 -0
- package/.codex/skills/gaslighting/agents/openai.yaml +8 -0
- package/.codex/skills/gaslighting/references/GASLIGHTING_TEMPLATE.md +425 -0
- package/.codex/skills/gaslighting/references/HARDCORE_DISCIPLINE_TEMPLATE.md +425 -0
- package/.codex/skills/gaslighting/references/HOSPITAL_HOMEPAGE_EXAMPLE.md +119 -0
- package/.codex/skills/gaslighting/references/STACK_POLICY_TEMPLATE.md +64 -0
- package/.codex/skills/gaslighting/scripts/generate-gaslighting-docs.ts +3 -0
- package/LICENSE +21 -0
- package/README.md +200 -0
- package/dist/cli.js +118 -0
- package/dist/commands/agents.js +10 -0
- package/dist/commands/codexInstall.js +60 -0
- package/dist/commands/doctor.js +42 -0
- package/dist/commands/generate.js +4 -0
- package/dist/commands/init.js +27 -0
- package/dist/commands/skill.js +10 -0
- package/dist/commands/update.js +35 -0
- package/dist/core/analyze.js +132 -0
- package/dist/core/classifyProjectType.js +66 -0
- package/dist/core/content.js +125 -0
- package/dist/core/detectStackHints.js +34 -0
- package/dist/core/generateDocs.js +58 -0
- package/dist/core/generateGaslightingMarkdown.js +420 -0
- package/dist/core/generateOtherMarkdown.js +529 -0
- package/dist/core/generatePrdMarkdown.js +125 -0
- package/dist/index.js +3 -0
- package/dist/types.js +1 -0
- package/dist/utils/banner.js +49 -0
- package/dist/utils/date.js +6 -0
- package/dist/utils/file.js +24 -0
- package/dist/utils/logger.js +27 -0
- package/dist/utils/markdown.js +6 -0
- package/docs/codex-usage.md +58 -0
- package/docs/examples.md +22 -0
- package/docs/philosophy.md +17 -0
- package/docs/research.md +54 -0
- package/examples/ecommerce/.codex/prompts/gaslighting.md +30 -0
- package/examples/ecommerce/.codex/skills/gaslighting/SKILL.md +96 -0
- package/examples/ecommerce/.codex/skills/gaslighting/agents/openai.yaml +8 -0
- package/examples/ecommerce/.codex/skills/gaslighting/references/GASLIGHTING_TEMPLATE.md +425 -0
- package/examples/ecommerce/.codex/skills/gaslighting/references/HARDCORE_DISCIPLINE_TEMPLATE.md +425 -0
- package/examples/ecommerce/.codex/skills/gaslighting/references/HOSPITAL_HOMEPAGE_EXAMPLE.md +119 -0
- package/examples/ecommerce/.codex/skills/gaslighting/references/STACK_POLICY_TEMPLATE.md +64 -0
- package/examples/ecommerce/.codex/skills/gaslighting/scripts/generate-gaslighting-docs.ts +3 -0
- package/examples/ecommerce/AGENTS.md +47 -0
- package/examples/ecommerce/ASSUMPTIONS.md +146 -0
- package/examples/ecommerce/CODEX_PROMPT.md +34 -0
- package/examples/ecommerce/DECISION_LOG.md +95 -0
- package/examples/ecommerce/GASLIGHTING.md +429 -0
- package/examples/ecommerce/MEMORY.md +63 -0
- package/examples/ecommerce/MISSING_INFO.md +13 -0
- package/examples/ecommerce/PRD.md +115 -0
- package/examples/ecommerce/STACK_POLICY.md +64 -0
- package/examples/hospital-homepage/.codex/prompts/gaslighting.md +30 -0
- package/examples/hospital-homepage/.codex/skills/gaslighting/SKILL.md +96 -0
- package/examples/hospital-homepage/.codex/skills/gaslighting/agents/openai.yaml +8 -0
- package/examples/hospital-homepage/.codex/skills/gaslighting/references/GASLIGHTING_TEMPLATE.md +425 -0
- package/examples/hospital-homepage/.codex/skills/gaslighting/references/HARDCORE_DISCIPLINE_TEMPLATE.md +425 -0
- package/examples/hospital-homepage/.codex/skills/gaslighting/references/HOSPITAL_HOMEPAGE_EXAMPLE.md +119 -0
- package/examples/hospital-homepage/.codex/skills/gaslighting/references/STACK_POLICY_TEMPLATE.md +64 -0
- package/examples/hospital-homepage/.codex/skills/gaslighting/scripts/generate-gaslighting-docs.ts +3 -0
- package/examples/hospital-homepage/AGENTS.md +47 -0
- package/examples/hospital-homepage/ASSUMPTIONS.md +218 -0
- package/examples/hospital-homepage/CODEX_PROMPT.md +34 -0
- package/examples/hospital-homepage/DECISION_LOG.md +95 -0
- package/examples/hospital-homepage/GASLIGHTING.md +432 -0
- package/examples/hospital-homepage/MEMORY.md +66 -0
- package/examples/hospital-homepage/MISSING_INFO.md +13 -0
- package/examples/hospital-homepage/PRD.md +119 -0
- package/examples/hospital-homepage/STACK_POLICY.md +64 -0
- package/examples/landing-page/.codex/prompts/gaslighting.md +30 -0
- package/examples/landing-page/.codex/skills/gaslighting/SKILL.md +96 -0
- package/examples/landing-page/.codex/skills/gaslighting/agents/openai.yaml +8 -0
- package/examples/landing-page/.codex/skills/gaslighting/references/GASLIGHTING_TEMPLATE.md +425 -0
- package/examples/landing-page/.codex/skills/gaslighting/references/HARDCORE_DISCIPLINE_TEMPLATE.md +425 -0
- package/examples/landing-page/.codex/skills/gaslighting/references/HOSPITAL_HOMEPAGE_EXAMPLE.md +119 -0
- package/examples/landing-page/.codex/skills/gaslighting/references/STACK_POLICY_TEMPLATE.md +64 -0
- package/examples/landing-page/.codex/skills/gaslighting/scripts/generate-gaslighting-docs.ts +3 -0
- package/examples/landing-page/AGENTS.md +47 -0
- package/examples/landing-page/ASSUMPTIONS.md +146 -0
- package/examples/landing-page/CODEX_PROMPT.md +34 -0
- package/examples/landing-page/DECISION_LOG.md +95 -0
- package/examples/landing-page/GASLIGHTING.md +424 -0
- package/examples/landing-page/MEMORY.md +63 -0
- package/examples/landing-page/MISSING_INFO.md +13 -0
- package/examples/landing-page/PRD.md +103 -0
- package/examples/landing-page/STACK_POLICY.md +64 -0
- 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,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
|
+
}
|