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.
- package/.agents/prompts/gaslighting.md +4 -1
- package/.agents/skills/gaslighting/SKILL.md +12 -1
- package/.codex/prompts/gaslighting.md +4 -1
- package/.codex/skills/gaslighting/SKILL.md +12 -1
- package/README.md +46 -3
- package/dist/cli.js +48 -1
- package/dist/commands/cockpit.js +174 -0
- package/dist/commands/codexInstall.js +3 -2
- package/dist/commands/doctor.js +1 -0
- package/dist/commands/upgrade.js +27 -0
- package/dist/core/cockpitHtml.js +596 -0
- package/dist/core/codexRuntime.js +74 -0
- package/dist/core/generateDocs.js +5 -3
- package/dist/core/generateOtherMarkdown.js +84 -4
- package/dist/index.js +1 -1
- package/dist/utils/logger.js +1 -1
- package/dist/utils/updateCheck.js +1 -2
- package/dist/version.js +1 -1
- package/docs/codex-usage.md +24 -4
- package/docs/examples.md +4 -0
- package/examples/ecommerce/.codex/prompts/gaslighting.md +4 -1
- package/examples/ecommerce/.codex/skills/gaslighting/SKILL.md +12 -1
- package/examples/ecommerce/.gaslighting/AGENTS.md +3 -1
- package/examples/ecommerce/.gaslighting/AGENT_RUNTIME.md +54 -0
- package/examples/ecommerce/.gaslighting/CODEX_PROMPT.md +8 -1
- package/examples/ecommerce/AGENTS.md +3 -2
- package/examples/hospital-homepage/.codex/prompts/gaslighting.md +4 -1
- package/examples/hospital-homepage/.codex/skills/gaslighting/SKILL.md +12 -1
- package/examples/hospital-homepage/.gaslighting/AGENTS.md +3 -1
- package/examples/hospital-homepage/.gaslighting/AGENT_RUNTIME.md +54 -0
- package/examples/hospital-homepage/.gaslighting/CODEX_PROMPT.md +8 -1
- package/examples/hospital-homepage/AGENTS.md +3 -2
- package/examples/landing-page/.codex/prompts/gaslighting.md +4 -1
- package/examples/landing-page/.codex/skills/gaslighting/SKILL.md +12 -1
- package/examples/landing-page/.gaslighting/AGENTS.md +3 -1
- package/examples/landing-page/.gaslighting/AGENT_RUNTIME.md +54 -0
- package/examples/landing-page/.gaslighting/CODEX_PROMPT.md +8 -1
- package/examples/landing-page/AGENTS.md +3 -2
- 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/
|
|
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/
|
|
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/
|
|
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/
|
|
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:
|
|
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
|
|
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")
|
package/dist/commands/doctor.js
CHANGED
|
@@ -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
|
+
}
|