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