gaslighting-engine 0.2.1 → 0.3.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 (39) hide show
  1. package/.agents/prompts/gaslighting.md +4 -1
  2. package/.agents/skills/gaslighting/SKILL.md +12 -1
  3. package/.codex/prompts/gaslighting.md +4 -1
  4. package/.codex/skills/gaslighting/SKILL.md +12 -1
  5. package/README.md +46 -3
  6. package/dist/cli.js +48 -1
  7. package/dist/commands/cockpit.js +174 -0
  8. package/dist/commands/codexInstall.js +3 -2
  9. package/dist/commands/doctor.js +1 -0
  10. package/dist/commands/upgrade.js +27 -0
  11. package/dist/core/cockpitHtml.js +596 -0
  12. package/dist/core/codexRuntime.js +74 -0
  13. package/dist/core/generateDocs.js +5 -3
  14. package/dist/core/generateOtherMarkdown.js +84 -4
  15. package/dist/index.js +1 -1
  16. package/dist/utils/logger.js +1 -1
  17. package/dist/utils/updateCheck.js +1 -2
  18. package/dist/version.js +1 -1
  19. package/docs/codex-usage.md +24 -4
  20. package/docs/examples.md +4 -0
  21. package/examples/ecommerce/.codex/prompts/gaslighting.md +4 -1
  22. package/examples/ecommerce/.codex/skills/gaslighting/SKILL.md +12 -1
  23. package/examples/ecommerce/.gaslighting/AGENTS.md +3 -1
  24. package/examples/ecommerce/.gaslighting/AGENT_RUNTIME.md +54 -0
  25. package/examples/ecommerce/.gaslighting/CODEX_PROMPT.md +8 -1
  26. package/examples/ecommerce/AGENTS.md +3 -2
  27. package/examples/hospital-homepage/.codex/prompts/gaslighting.md +4 -1
  28. package/examples/hospital-homepage/.codex/skills/gaslighting/SKILL.md +12 -1
  29. package/examples/hospital-homepage/.gaslighting/AGENTS.md +3 -1
  30. package/examples/hospital-homepage/.gaslighting/AGENT_RUNTIME.md +54 -0
  31. package/examples/hospital-homepage/.gaslighting/CODEX_PROMPT.md +8 -1
  32. package/examples/hospital-homepage/AGENTS.md +3 -2
  33. package/examples/landing-page/.codex/prompts/gaslighting.md +4 -1
  34. package/examples/landing-page/.codex/skills/gaslighting/SKILL.md +12 -1
  35. package/examples/landing-page/.gaslighting/AGENTS.md +3 -1
  36. package/examples/landing-page/.gaslighting/AGENT_RUNTIME.md +54 -0
  37. package/examples/landing-page/.gaslighting/CODEX_PROMPT.md +8 -1
  38. package/examples/landing-page/AGENTS.md +3 -2
  39. package/package.json +1 -1
@@ -10,7 +10,8 @@ Read the Gaslighting-engine project-control files before doing any work:
10
10
  6. `.gaslighting/DECISION_LOG.md`
11
11
  7. `AGENTS.md`
12
12
  8. `.gaslighting/MEMORY.md`
13
- 9. `.gaslighting/PROJECT_CARE.md`
13
+ 9. `.gaslighting/AGENT_RUNTIME.md`
14
+ 10. `.gaslighting/PROJECT_CARE.md`
14
15
 
15
16
  Then execute the user's requested implementation.
16
17
 
@@ -30,3 +31,5 @@ Rules:
30
31
  - If something is incomplete, declare it explicitly.
31
32
 
32
33
  Before claiming completion, perform the self-audit in `.gaslighting/GASLIGHTING.md`.
34
+
35
+ To refresh this Gaslighting Codex install later, run `npx gaslighting-engine@latest upgrade`.
@@ -23,7 +23,8 @@ Instead:
23
23
  10. Generate `.gaslighting/DECISION_LOG.md`.
24
24
  11. Generate `.gaslighting/STACK_POLICY.md`.
25
25
  12. Generate or update root `AGENTS.md`.
26
- 13. Generate `.gaslighting/PROJECT_CARE.md`.
26
+ 13. Generate `.gaslighting/AGENT_RUNTIME.md`.
27
+ 14. Generate `.gaslighting/PROJECT_CARE.md`.
27
28
 
28
29
  ## Hardcore Discipline Rule
29
30
 
@@ -92,6 +93,8 @@ Keep main project-control files in `.gaslighting/` and keep only the Codex point
92
93
 
93
94
  Use `.gaslighting/PROJECT_CARE.md` to track Git/GitHub/domain/deployment/launch risks without blocking implementation.
94
95
 
96
+ Use `.gaslighting/AGENT_RUNTIME.md` to define the execution loop Codex must follow after the docs are generated.
97
+
95
98
  Do not only explain.
96
99
 
97
100
  Do not produce a plan without files.
@@ -99,3 +102,11 @@ Do not produce a plan without files.
99
102
  Do not say what should be done.
100
103
 
101
104
  Generate the actual project discipline documents.
105
+
106
+ ## Update Rule
107
+
108
+ When the user asks how to update this Gaslighting Codex install, give the simple command:
109
+
110
+ ```bash
111
+ npx gaslighting-engine@latest upgrade
112
+ ```
@@ -10,7 +10,8 @@ Read the Gaslighting-engine project-control files before doing any work:
10
10
  6. `.gaslighting/DECISION_LOG.md`
11
11
  7. `AGENTS.md`
12
12
  8. `.gaslighting/MEMORY.md`
13
- 9. `.gaslighting/PROJECT_CARE.md`
13
+ 9. `.gaslighting/AGENT_RUNTIME.md`
14
+ 10. `.gaslighting/PROJECT_CARE.md`
14
15
 
15
16
  Then execute the user's requested implementation.
16
17
 
@@ -30,3 +31,5 @@ Rules:
30
31
  - If something is incomplete, declare it explicitly.
31
32
 
32
33
  Before claiming completion, perform the self-audit in `.gaslighting/GASLIGHTING.md`.
34
+
35
+ To refresh this Gaslighting Codex install later, run `npx gaslighting-engine@latest upgrade`.
@@ -23,7 +23,8 @@ Instead:
23
23
  10. Generate `.gaslighting/DECISION_LOG.md`.
24
24
  11. Generate `.gaslighting/STACK_POLICY.md`.
25
25
  12. Generate or update root `AGENTS.md`.
26
- 13. Generate `.gaslighting/PROJECT_CARE.md`.
26
+ 13. Generate `.gaslighting/AGENT_RUNTIME.md`.
27
+ 14. Generate `.gaslighting/PROJECT_CARE.md`.
27
28
 
28
29
  ## Hardcore Discipline Rule
29
30
 
@@ -92,6 +93,8 @@ Keep main project-control files in `.gaslighting/` and keep only the Codex point
92
93
 
93
94
  Use `.gaslighting/PROJECT_CARE.md` to track Git/GitHub/domain/deployment/launch risks without blocking implementation.
94
95
 
96
+ Use `.gaslighting/AGENT_RUNTIME.md` to define the execution loop Codex must follow after the docs are generated.
97
+
95
98
  Do not only explain.
96
99
 
97
100
  Do not produce a plan without files.
@@ -99,3 +102,11 @@ Do not produce a plan without files.
99
102
  Do not say what should be done.
100
103
 
101
104
  Generate the actual project discipline documents.
105
+
106
+ ## Update Rule
107
+
108
+ When the user asks how to update this Gaslighting Codex install, give the simple command:
109
+
110
+ ```bash
111
+ npx gaslighting-engine@latest upgrade
112
+ ```
package/README.md CHANGED
@@ -30,6 +30,7 @@ It generates:
30
30
  - `.gaslighting/MISSING_INFO.md`
31
31
  - `.gaslighting/DECISION_LOG.md`
32
32
  - `.gaslighting/MEMORY.md`
33
+ - `.gaslighting/AGENT_RUNTIME.md`
33
34
  - `.gaslighting/STACK_POLICY.md`
34
35
  - `.gaslighting/PROJECT_CARE.md`
35
36
  - `AGENTS.md`
@@ -40,9 +41,11 @@ It generates:
40
41
  ## Quick Start
41
42
 
42
43
  ```bash
43
- npx gaslighting-engine "I want to build a hospital website."
44
+ npx gaslighting-engine@latest start "I want to build a hospital website."
44
45
  ```
45
46
 
47
+ This generates the discipline documents, opens the local Mission Control GUI, and prepares a Codex launch command.
48
+
46
49
  Short aliases after global install or local link:
47
50
 
48
51
  ```bash
@@ -84,6 +87,10 @@ gaslighting init "I want to build a hospital website."
84
87
  gaslighting-engine generate "Build an ecommerce MVP."
85
88
  gaslighting-engine update "The hospital is actually an OB-GYN clinic, not dermatology."
86
89
  gaslighting-engine doctor
90
+ gaslighting-engine start "Build a hospital homepage"
91
+ gaslighting-engine cockpit
92
+ gaslighting-engine run "Build a hospital homepage"
93
+ gaslighting-engine upgrade
87
94
  gaslighting-engine codex-install
88
95
  gaslighting-engine codex-doctor
89
96
  gaslighting-engine skill
@@ -98,6 +105,35 @@ Gaslighting-engine keeps project-control documents in `.gaslighting/` by default
98
105
 
99
106
  Only root `AGENTS.md` is written at the top level because Codex expects project guidance there. It is a thin pointer into `.gaslighting/`.
100
107
 
108
+ ## Mission Control
109
+
110
+ Mission Control is the Codex-first GUI agent surface:
111
+
112
+ ```bash
113
+ npx gaslighting-engine@latest start "I want to build a hospital website."
114
+ ```
115
+
116
+ It shows:
117
+
118
+ - project purpose
119
+ - generated control documents
120
+ - missing information
121
+ - Git/GitHub and deployment care checks
122
+ - Codex runtime args
123
+ - Start Codex action
124
+
125
+ Open it for an existing project:
126
+
127
+ ```bash
128
+ npx gaslighting-engine@latest cockpit
129
+ ```
130
+
131
+ Run without the GUI:
132
+
133
+ ```bash
134
+ npx gaslighting-engine@latest run "I want to build a hospital website."
135
+ ```
136
+
101
137
  ## Project Care
102
138
 
103
139
  Gaslighting-engine also writes `.gaslighting/PROJECT_CARE.md`.
@@ -126,14 +162,21 @@ This initializes Git when needed and connects `origin`. If `origin` already exis
126
162
 
127
163
  Gaslighting-engine checks npm for a newer version when you run normal commands.
128
164
 
165
+ The simple update command is:
166
+
167
+ ```bash
168
+ npx gaslighting-engine@latest upgrade
169
+ ```
170
+
171
+ This updates the Codex skill and prompt files in the current project. No `--force` is needed.
172
+
129
173
  If an update exists, it prints a short notice like:
130
174
 
131
175
  ```txt
132
176
  Gaslighting-engine update available:
133
177
  - current: 0.1.0
134
178
  - latest: 0.1.1
135
- - update: npm install -g gaslighting-engine@latest
136
- - npx: npx gaslighting-engine@latest
179
+ - update: npx gaslighting-engine@latest upgrade
137
180
  ```
138
181
 
139
182
  The update check is best-effort and never blocks the command. Disable it with:
package/dist/cli.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import { Command, InvalidArgumentError } from "commander";
2
2
  import { runAgents } from "./commands/agents.js";
3
3
  import { runCare } from "./commands/care.js";
4
+ import { runCockpit, runCodexRun, runStart } from "./commands/cockpit.js";
4
5
  import { runCodexDoctor, runCodexInstall } from "./commands/codexInstall.js";
5
6
  import { runDoctor } from "./commands/doctor.js";
6
7
  import { runGenerate } from "./commands/generate.js";
7
8
  import { runInit } from "./commands/init.js";
8
9
  import { runSkill } from "./commands/skill.js";
9
10
  import { runUpdate } from "./commands/update.js";
11
+ import { runUpgrade } from "./commands/upgrade.js";
10
12
  import { packageVersion } from "./version.js";
11
13
  const projectTypes = [
12
14
  "hospital_homepage",
@@ -59,6 +61,8 @@ Examples:
59
61
  $ gaslighting-engine init "Build a landing page" --dry-run
60
62
  $ gaslighting-engine doctor
61
63
  $ gaslighting-engine care --github-url https://github.com/user/repo.git
64
+ $ gaslighting-engine start "Build a hospital homepage"
65
+ $ gaslighting-engine upgrade
62
66
  $ gaslighting-engine codex-install --force
63
67
 
64
68
  Defaults:
@@ -102,6 +106,49 @@ Defaults:
102
106
  .option("--github-url <url>", "initialize git if needed and connect origin to this GitHub URL")
103
107
  .option("--force-remote", "replace an existing origin remote when used with --github-url")
104
108
  .action(runCare);
109
+ program
110
+ .command("start <request>")
111
+ .description("Generate discipline docs, open Mission Control, and prepare Codex launch")
112
+ .option("--force", "overwrite existing files")
113
+ .option("--no-force", "preserve existing files and create .new variants instead")
114
+ .option("--no-open", "print the Mission Control URL without opening a browser")
115
+ .option("--port <port>", "Mission Control port, random by default")
116
+ .option("--path <path>", "target directory")
117
+ .option("--lang <lang>", "language: en or ko", "en")
118
+ .option("--type <type>", "project type override", parseProjectType)
119
+ .option("--codex-args <args>", "Codex CLI args used by the Start Codex button")
120
+ .action(runStart);
121
+ program
122
+ .command("cockpit [request]")
123
+ .description("Open the local Gaslighting Mission Control GUI")
124
+ .option("--no-generate", "open without regenerating discipline docs")
125
+ .option("--force", "overwrite existing files")
126
+ .option("--no-force", "preserve existing files and create .new variants instead")
127
+ .option("--no-open", "print the Mission Control URL without opening a browser")
128
+ .option("--port <port>", "Mission Control port, random by default")
129
+ .option("--path <path>", "target directory")
130
+ .option("--lang <lang>", "language: en or ko", "en")
131
+ .option("--type <type>", "project type override", parseProjectType)
132
+ .option("--codex-args <args>", "Codex CLI args used by the Start Codex button")
133
+ .action(runCockpit);
134
+ program
135
+ .command("run <request>")
136
+ .description("Generate discipline docs and launch Codex without opening the GUI")
137
+ .option("--force", "overwrite existing files")
138
+ .option("--no-force", "preserve existing files and create .new variants instead")
139
+ .option("--path <path>", "target directory")
140
+ .option("--lang <lang>", "language: en or ko", "en")
141
+ .option("--type <type>", "project type override", parseProjectType)
142
+ .option("--codex-args <args>", "Codex CLI args")
143
+ .action(runCodexRun);
144
+ program
145
+ .command("upgrade")
146
+ .description("Update the local Codex Gaslighting install with one simple command")
147
+ .option("--no-force", "preserve existing files and create .new variants instead")
148
+ .option("--dry-run", "print files without writing")
149
+ .option("--path <path>", "target directory")
150
+ .option("--scope <scope>", "project or user", "project")
151
+ .action(runUpgrade);
105
152
  program
106
153
  .command("codex-install")
107
154
  .description("Install Gaslighting as a Codex-optimized project or user skill")
@@ -124,7 +171,7 @@ export function normalizeDefaultInitArgv(argv) {
124
171
  return argv;
125
172
  if (args.some((arg) => ["--help", "-h", "--version", "-V"].includes(arg)))
126
173
  return argv;
127
- const commands = new Set(["init", "generate", "update", "doctor", "skill", "agents", "care", "codex-install", "codex-doctor"]);
174
+ const commands = new Set(["init", "generate", "update", "doctor", "skill", "agents", "care", "start", "cockpit", "run", "upgrade", "codex-install", "codex-doctor"]);
128
175
  const firstMeaningful = args.find((arg) => !arg.startsWith("-"));
129
176
  if (firstMeaningful && commands.has(firstMeaningful))
130
177
  return argv;
@@ -0,0 +1,174 @@
1
+ import { execFileSync, spawn } from "node:child_process";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { createServer } from "node:http";
4
+ import { join, resolve } from "node:path";
5
+ import { platform } from "node:os";
6
+ import { analyze } from "../core/analyze.js";
7
+ import { getDefaultCodexArgs, launchCodex } from "../core/codexRuntime.js";
8
+ import { pageList, projectPurpose } from "../core/content.js";
9
+ import { generateCodexInstallDocs, generateDocs, generateProjectCareDoc } from "../core/generateDocs.js";
10
+ import { renderCockpitHtml } from "../core/cockpitHtml.js";
11
+ import { connectGitHubRemote, inspectProjectCare } from "../core/projectCare.js";
12
+ import { printBanner } from "../utils/banner.js";
13
+ import { writeDocs } from "../utils/file.js";
14
+ import { packageVersion } from "../version.js";
15
+ const docFiles = [
16
+ "AGENTS.md",
17
+ ".gaslighting/GASLIGHTING.md",
18
+ ".gaslighting/PRD.md",
19
+ ".gaslighting/MISSING_INFO.md",
20
+ ".gaslighting/ASSUMPTIONS.md",
21
+ ".gaslighting/DECISION_LOG.md",
22
+ ".gaslighting/MEMORY.md",
23
+ ".gaslighting/AGENT_RUNTIME.md",
24
+ ".gaslighting/PROJECT_CARE.md",
25
+ ];
26
+ export async function runCockpit(rawUserRequest = "", options) {
27
+ await openCockpit(rawUserRequest, { ...options, generate: Boolean(rawUserRequest) && options.generate !== false, force: options.force ?? true });
28
+ }
29
+ export async function runStart(rawUserRequest, options) {
30
+ await openCockpit(rawUserRequest, { ...options, generate: true, force: options.force ?? true });
31
+ }
32
+ export function runCodexRun(rawUserRequest, options) {
33
+ const root = resolve(options.path ?? process.cwd());
34
+ const input = makeInput(rawUserRequest, options);
35
+ writeDocs(root, generateDocs(input, root).docs, options.force ?? true, false);
36
+ writeDocs(root, generateCodexInstallDocs(), true, false);
37
+ const result = launchCodex({ root, request: rawUserRequest, codexArgs: options.codexArgs });
38
+ printBanner("CODEX RUN");
39
+ console.log(result.message);
40
+ console.log(result.command);
41
+ }
42
+ async function openCockpit(rawUserRequest, options) {
43
+ const root = resolve(options.path ?? process.cwd());
44
+ let currentRequest = rawUserRequest || readExistingRequest(root) || "Build a practical MVP without shrinking scope.";
45
+ let currentInput = makeInput(currentRequest, options);
46
+ if (options.generate) {
47
+ writeDocs(root, generateDocs(currentInput, root).docs, options.force ?? true, false);
48
+ writeDocs(root, generateCodexInstallDocs(), true, false);
49
+ }
50
+ const requestedPort = Number.parseInt(options.port ?? "0", 10);
51
+ const server = createServer(async (request, response) => {
52
+ try {
53
+ if (request.method === "GET" && request.url === "/")
54
+ return sendHtml(response, renderCockpitHtml());
55
+ if (request.method === "GET" && request.url === "/api/state")
56
+ return sendJson(response, buildState(root, currentRequest, currentInput, options));
57
+ if (request.method === "POST" && request.url === "/api/generate") {
58
+ const body = await readJson(request);
59
+ currentRequest = String(body.request || currentRequest);
60
+ currentInput = makeInput(currentRequest, options);
61
+ writeDocs(root, generateDocs(currentInput, root).docs, true, false);
62
+ return sendJson(response, { state: buildState(root, currentRequest, currentInput, options) });
63
+ }
64
+ if (request.method === "POST" && request.url === "/api/start-codex") {
65
+ const body = await readJson(request);
66
+ currentRequest = String(body.request || currentRequest);
67
+ currentInput = makeInput(currentRequest, options);
68
+ writeDocs(root, generateDocs(currentInput, root).docs, true, false);
69
+ const result = launchCodex({ root, request: currentRequest, codexArgs: options.codexArgs });
70
+ return sendJson(response, { ...result });
71
+ }
72
+ if (request.method === "POST" && request.url === "/api/connect-github") {
73
+ const body = await readJson(request);
74
+ const githubUrl = String(body.githubUrl || "");
75
+ const results = connectGitHubRemote(root, githubUrl, Boolean(body.forceRemote));
76
+ writeDocs(root, generateProjectCareDoc(currentInput, root), true, false);
77
+ return sendJson(response, { results, state: buildState(root, currentRequest, currentInput, options) });
78
+ }
79
+ sendJson(response, { message: "Not found." }, 404);
80
+ }
81
+ catch (error) {
82
+ const message = error instanceof Error ? error.message : "Unknown cockpit error.";
83
+ sendJson(response, { message }, 500);
84
+ }
85
+ });
86
+ await new Promise((resolveListen) => server.listen(requestedPort, "127.0.0.1", resolveListen));
87
+ const address = server.address();
88
+ const port = typeof address === "object" && address ? address.port : requestedPort;
89
+ const url = `http://127.0.0.1:${port}`;
90
+ printBanner("MISSION CONTROL");
91
+ console.log(`Project: ${currentRequest}`);
92
+ console.log(`Root: ${root}`);
93
+ console.log(`URL: ${url}`);
94
+ console.log("Close this terminal session to stop the cockpit.");
95
+ if (options.open !== false)
96
+ openBrowser(url);
97
+ }
98
+ function buildState(root, request, input, options) {
99
+ const analysis = analyze(input);
100
+ const docsPresent = Object.fromEntries(docFiles.map((file) => [file, existsSync(join(root, file))]));
101
+ return {
102
+ version: packageVersion,
103
+ root,
104
+ request,
105
+ analysis,
106
+ purpose: projectPurpose(analysis.projectType),
107
+ pages: pageList(analysis.projectType),
108
+ care: inspectProjectCare(root),
109
+ docsPresent,
110
+ codexArgs: options.codexArgs || getDefaultCodexArgs(),
111
+ codexAvailable: isCommandAvailable("codex"),
112
+ };
113
+ }
114
+ function makeInput(rawUserRequest, options) {
115
+ return {
116
+ rawUserRequest,
117
+ projectType: options.type,
118
+ language: options.lang ?? "en",
119
+ mode: "hardcore",
120
+ fullScope: true,
121
+ noTodo: true,
122
+ noShortcut: true,
123
+ force: options.force ?? true,
124
+ };
125
+ }
126
+ function readExistingRequest(root) {
127
+ const carePath = join(root, ".gaslighting", "PROJECT_CARE.md");
128
+ if (!existsSync(carePath))
129
+ return undefined;
130
+ const content = readFileSync(carePath, "utf8");
131
+ return /^- Original request: (.+)$/m.exec(content)?.[1];
132
+ }
133
+ function isCommandAvailable(command) {
134
+ try {
135
+ if (platform() === "win32")
136
+ execFileSync("where", [command], { stdio: "ignore" });
137
+ else
138
+ execFileSync("sh", ["-lc", `command -v ${command}`], { stdio: "ignore" });
139
+ return true;
140
+ }
141
+ catch {
142
+ return false;
143
+ }
144
+ }
145
+ function openBrowser(url) {
146
+ const command = platform() === "win32"
147
+ ? { file: "cmd.exe", args: ["/c", "start", "", url] }
148
+ : platform() === "darwin"
149
+ ? { file: "open", args: [url] }
150
+ : { file: "xdg-open", args: [url] };
151
+ try {
152
+ const child = spawn(command.file, command.args, { detached: true, stdio: "ignore" });
153
+ child.unref();
154
+ }
155
+ catch {
156
+ // The printed URL is the fallback.
157
+ }
158
+ }
159
+ async function readJson(request) {
160
+ const chunks = [];
161
+ for await (const chunk of request)
162
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
163
+ if (chunks.length === 0)
164
+ return {};
165
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
166
+ }
167
+ function sendHtml(response, html) {
168
+ response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
169
+ response.end(html);
170
+ }
171
+ function sendJson(response, data, status = 200) {
172
+ response.writeHead(status, { "content-type": "application/json; charset=utf-8" });
173
+ response.end(JSON.stringify(data));
174
+ }
@@ -26,6 +26,7 @@ export function runCodexInstall(options) {
26
26
  console.log('- If Codex exposes enabled skills in slash commands, use: /gaslighting');
27
27
  console.log("- If neither appears immediately, restart Codex and reopen this project.");
28
28
  console.log("- Fallback prompt file: .gaslighting/CODEX_GASLIGHTING.md");
29
+ console.log("- Update later with: npx gaslighting-engine@latest upgrade");
29
30
  }
30
31
  export function runCodexDoctor(options) {
31
32
  const target = resolveTargetPath(options);
@@ -46,12 +47,12 @@ export function runCodexDoctor(options) {
46
47
  const hasSkill = checks.some((check) => check.ok && check.file.endsWith("skills/gaslighting/SKILL.md"));
47
48
  if (!hasSkill) {
48
49
  process.exitCode = 1;
49
- console.log("\nNo gaslighting skill was found. Run: gaslighting codex-install --force");
50
+ console.log("\nNo gaslighting skill was found. Run: npx gaslighting-engine@latest upgrade");
50
51
  return;
51
52
  }
52
53
  console.log("\nCodex install looks usable. Restart Codex if the skill does not appear yet.");
53
54
  }
54
- function resolveTargetPath(options) {
55
+ export function resolveTargetPath(options) {
55
56
  if (options.path)
56
57
  return resolve(options.path);
57
58
  if (options.scope === "user")
@@ -18,6 +18,7 @@ export function runDoctor(options) {
18
18
  fileCheck(root, ".gaslighting/MISSING_INFO.md"),
19
19
  fileCheck(root, ".gaslighting/DECISION_LOG.md"),
20
20
  fileCheck(root, ".gaslighting/MEMORY.md"),
21
+ fileCheck(root, ".gaslighting/AGENT_RUNTIME.md"),
21
22
  fileCheck(root, ".gaslighting/PROJECT_CARE.md"),
22
23
  { name: ".gaslighting/GASLIGHTING.md contains project purpose", ok: /Project Purpose/i.test(gaslighting) },
23
24
  { name: ".gaslighting/GASLIGHTING.md contains no-fake-completion rules", ok: /No Fake Completion|Fake completion/i.test(gaslighting) },
@@ -0,0 +1,27 @@
1
+ import { generateCodexInstallDocs } from "../core/generateDocs.js";
2
+ import { printBanner } from "../utils/banner.js";
3
+ import { writeDocs } from "../utils/file.js";
4
+ import { packageVersion } from "../version.js";
5
+ import { resolveTargetPath } from "./codexInstall.js";
6
+ export function runUpgrade(options) {
7
+ const target = resolveTargetPath(options);
8
+ const force = options.force ?? true;
9
+ const results = writeDocs(target, generateCodexInstallDocs(), force, options.dryRun);
10
+ printBanner("UPGRADE");
11
+ console.log(`Version: ${packageVersion}`);
12
+ console.log(`Target: ${target}\n`);
13
+ console.log("Updated Codex files:");
14
+ for (const result of results) {
15
+ const suffix = result.status === "created_variant"
16
+ ? " (existing file preserved)"
17
+ : result.status === "overwritten"
18
+ ? " (overwritten)"
19
+ : result.status === "dry_run"
20
+ ? " (dry run)"
21
+ : "";
22
+ console.log(`- ${result.filename}${suffix}`);
23
+ }
24
+ console.log("\nDone.");
25
+ console.log("Restart Codex or reopen this project if the old skill is still visible.");
26
+ console.log('Use: /gaslighting, "$gaslighting", or say "Use the gaslighting skill."');
27
+ }