codictator 0.1.3 → 0.2.1
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/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +3 -1
- package/dist/cli/guardrail.d.ts +2 -0
- package/dist/cli/guardrail.js +17 -15
- package/dist/cli/init-plan.d.ts +7 -0
- package/dist/cli/init-plan.js +21 -2
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.js +8 -17
- package/dist/cli/plan.d.ts +3 -1
- package/dist/cli/plan.js +48 -9
- package/dist/cli/serve-run.js +1 -0
- package/dist/cli/setup-gitignore.d.ts +7 -0
- package/dist/cli/setup-gitignore.js +41 -0
- package/dist/cli/setup-handoff.d.ts +7 -0
- package/dist/cli/setup-handoff.js +44 -0
- package/dist/cli/setup-install.d.ts +11 -0
- package/dist/cli/setup-install.js +84 -0
- package/dist/cli/setup.d.ts +9 -0
- package/dist/cli/setup.js +143 -0
- package/dist/features/adapters/harness-files.d.ts +15 -0
- package/dist/features/adapters/harness-files.js +85 -0
- package/dist/features/adapters/index.d.ts +2 -1
- package/dist/features/adapters/index.js +2 -1
- package/dist/features/engine/evaluate.js +42 -54
- package/dist/features/engine/run-enforcer.d.ts +7 -0
- package/dist/features/engine/run-enforcer.js +35 -0
- package/dist/features/engine/waivers.d.ts +5 -1
- package/dist/features/engine/waivers.js +32 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
package/dist/cli/commands.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ import type { Result } from "../kernel/index.ts";
|
|
|
2
2
|
import type { CommandRequest } from "./shared.ts";
|
|
3
3
|
export type Handler = (req: CommandRequest) => Promise<Result<number>>;
|
|
4
4
|
export declare const COMMANDS: Readonly<Record<string, Handler>>;
|
|
5
|
-
export declare const USAGE = "usage: codictator <init|install-hooks|regen|uninstall|verify|status|explain|replay|report|context|hook|task|test|review|waive|baseline|deps|remediate|ci|generate|pack|adapter|serve> [--json] [--cwd <path>] [--quiet]";
|
|
5
|
+
export declare const USAGE = "usage: codictator <init|setup|install-hooks|regen|uninstall|verify|status|explain|replay|report|context|hook|task|test|review|waive|baseline|deps|remediate|ci|generate|pack|adapter|serve> [--json] [--cwd <path>] [--quiet]";
|
package/dist/cli/commands.js
CHANGED
|
@@ -9,11 +9,13 @@ import { handleReview } from "./proofs.js";
|
|
|
9
9
|
import { handleTestCmd } from "./proofs-test.js";
|
|
10
10
|
import { handleRemediate } from "./remediation.js";
|
|
11
11
|
import { handleContext, handleReport } from "./report.js";
|
|
12
|
+
import { handleSetup } from "./setup.js";
|
|
12
13
|
import { handleTask } from "./task-advance.js";
|
|
13
14
|
import { handleExplain, handleReplay } from "./transparency.js";
|
|
14
15
|
import { handleWaive } from "./waivers.js";
|
|
15
16
|
export const COMMANDS = {
|
|
16
17
|
init: handleInit,
|
|
18
|
+
setup: handleSetup,
|
|
17
19
|
"install-hooks": handleInstallHooks,
|
|
18
20
|
regen: handleRegen,
|
|
19
21
|
uninstall: handleUninstall,
|
|
@@ -36,4 +38,4 @@ export const COMMANDS = {
|
|
|
36
38
|
adapter: handleAdapter,
|
|
37
39
|
serve: handleServe,
|
|
38
40
|
};
|
|
39
|
-
export const USAGE = "usage: codictator <init|install-hooks|regen|uninstall|verify|status|explain|replay|report|context|hook|task|test|review|waive|baseline|deps|remediate|ci|generate|pack|adapter|serve> [--json] [--cwd <path>] [--quiet]";
|
|
41
|
+
export const USAGE = "usage: codictator <init|setup|install-hooks|regen|uninstall|verify|status|explain|replay|report|context|hook|task|test|review|waive|baseline|deps|remediate|ci|generate|pack|adapter|serve> [--json] [--cwd <path>] [--quiet]";
|
package/dist/cli/guardrail.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Result } from "../kernel/index.ts";
|
|
2
2
|
import type { CommandRequest } from "./shared.ts";
|
|
3
3
|
export declare function handleRegen(req: CommandRequest): Promise<Result<number>>;
|
|
4
|
+
export declare function writeHookFiles(root: string): Promise<Result<true>>;
|
|
5
|
+
export declare function configureHooksPath(root: string, req: CommandRequest): Promise<void>;
|
|
4
6
|
export declare function handleInstallHooks(req: CommandRequest): Promise<Result<number>>;
|
|
5
7
|
export declare function handleUninstall(req: CommandRequest): Promise<Result<number>>;
|
package/dist/cli/guardrail.js
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
// install-hooks writes the hooks and points core.hooksPath at them;
|
|
4
4
|
// uninstall removes generated files with the CI warning.
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
|
-
import { renderCustomRegenFragment } from "../features/adapters/index.js";
|
|
6
|
+
import { renderCustomRegenFragment, withCodictatorOwner, } from "../features/adapters/index.js";
|
|
7
7
|
import { ENGINE_VERSION } from "../features/config/index.js";
|
|
8
8
|
import { EXPECTED_HOOKS_PATH, HOOK_NAMES, hookInstallPlan, renderHookScript, } from "../features/integrity/index.js";
|
|
9
9
|
import { err, ok, resolveRepoRoot } from "../kernel/index.js";
|
|
10
|
+
import { initPreamble } from "./init-plan.js";
|
|
10
11
|
import { guardrailPlan, writePlan } from "./plan.js";
|
|
11
|
-
import {
|
|
12
|
+
import { fail, loadRepoConfig, portsFor, printHuman, printJson, readStateText, usageError, } from "./shared.js";
|
|
12
13
|
function repoNameOf(root) {
|
|
13
14
|
return root.replaceAll("\\", "/").split("/").pop() ?? "repo";
|
|
14
15
|
}
|
|
@@ -67,8 +68,12 @@ export async function handleRegen(req) {
|
|
|
67
68
|
tunables: configured.tunables,
|
|
68
69
|
architecture: configured.architecture,
|
|
69
70
|
tools: configured.tools,
|
|
71
|
+
}, req.engineVersion ?? ENGINE_VERSION, root.value);
|
|
72
|
+
// SPEC §4.14.1: custom fragments keep the historical codictator owner.
|
|
73
|
+
const customOwned = withCodictatorOwner(customFiles.value);
|
|
74
|
+
const wrote = await writePlan(root.value, {
|
|
75
|
+
files: [...plan.files, ...customOwned],
|
|
70
76
|
}, req.engineVersion ?? ENGINE_VERSION);
|
|
71
|
-
const wrote = await writePlan(root.value, { files: [...plan.files, ...customFiles.value] }, req.engineVersion ?? ENGINE_VERSION);
|
|
72
77
|
if (!wrote.ok) {
|
|
73
78
|
return fail("CDX-E903", wrote.error.message, wrote.error.remediation, req.json);
|
|
74
79
|
}
|
|
@@ -89,7 +94,7 @@ export async function handleRegen(req) {
|
|
|
89
94
|
function encode(text) {
|
|
90
95
|
return new TextEncoder().encode(text);
|
|
91
96
|
}
|
|
92
|
-
async function writeHookFiles(root) {
|
|
97
|
+
export async function writeHookFiles(root) {
|
|
93
98
|
const ports = await portsFor(root);
|
|
94
99
|
if (!ports.ok) {
|
|
95
100
|
return err(ports.error);
|
|
@@ -107,7 +112,7 @@ async function writeHookFiles(root) {
|
|
|
107
112
|
}
|
|
108
113
|
return ok(true);
|
|
109
114
|
}
|
|
110
|
-
async function configureHooksPath(root, req) {
|
|
115
|
+
export async function configureHooksPath(root, req) {
|
|
111
116
|
const ports = await portsFor(root);
|
|
112
117
|
if (!ports.ok) {
|
|
113
118
|
return;
|
|
@@ -169,21 +174,18 @@ export async function handleUninstall(req) {
|
|
|
169
174
|
if (rejected !== undefined) {
|
|
170
175
|
return rejected;
|
|
171
176
|
}
|
|
172
|
-
const
|
|
173
|
-
if (
|
|
174
|
-
return
|
|
177
|
+
const preamble = await initPreamble(req);
|
|
178
|
+
if (typeof preamble !== "string") {
|
|
179
|
+
return preamble;
|
|
175
180
|
}
|
|
176
|
-
const root =
|
|
177
|
-
|
|
178
|
-
return fail("CDX-E703", root.error.message, root.error.remediation, req.json);
|
|
179
|
-
}
|
|
180
|
-
await removeGuardrails(root.value);
|
|
181
|
+
const root = preamble;
|
|
182
|
+
await removeGuardrails(root);
|
|
181
183
|
const note = "CI will fail until its workflow and required check are removed too.";
|
|
182
184
|
if (req.json) {
|
|
183
|
-
printJson({ uninstalled: true, root
|
|
185
|
+
printJson({ uninstalled: true, root, note });
|
|
184
186
|
}
|
|
185
187
|
else {
|
|
186
|
-
printHuman(`uninstalled guardrails from ${root
|
|
188
|
+
printHuman(`uninstalled guardrails from ${root}`, req.quiet);
|
|
187
189
|
printHuman(note, req.quiet);
|
|
188
190
|
}
|
|
189
191
|
return ok(0);
|
package/dist/cli/init-plan.d.ts
CHANGED
|
@@ -20,4 +20,11 @@ export declare function emitInitResult(req: CommandRequest, root: string, writte
|
|
|
20
20
|
path: string;
|
|
21
21
|
}>, baselineWritten: boolean): Result<number>;
|
|
22
22
|
export declare function initPreamble(req: CommandRequest): Promise<string | Result<number>>;
|
|
23
|
+
export interface InitSequence {
|
|
24
|
+
readonly written: ReadonlyArray<{
|
|
25
|
+
path: string;
|
|
26
|
+
}>;
|
|
27
|
+
readonly baseline: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare function runInitSequence(root: string, req: CommandRequest, flags: InitFlags, planned: ReadonlyArray<string>, engineVersion: string): Promise<Result<InitSequence>>;
|
|
23
30
|
export declare function buildGuardrailPlan(root: string, flags: InitFlags, planned: ReadonlyArray<string>, engineVersion?: string): Promise<ReturnType<typeof guardrailPlan>>;
|
package/dist/cli/init-plan.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { CODICTATOR_MODULES } from "../features/arch/index.js";
|
|
5
5
|
import { ENGINE_VERSION } from "../features/config/index.js";
|
|
6
6
|
import { captureBaseline } from "../features/remediation/index.js";
|
|
7
|
-
import { ok, parseJson, resolveRepoRoot, stableStringify, } from "../kernel/index.js";
|
|
8
|
-
import { buildHelperTool, guardrailPlan } from "./plan.js";
|
|
7
|
+
import { err, ok, parseJson, resolveRepoRoot, stableStringify, } from "../kernel/index.js";
|
|
8
|
+
import { buildHelperTool, guardrailPlan, writePlan } from "./plan.js";
|
|
9
9
|
import { checkHuman, commandEnv, fail, loadRepoConfig, printHuman, printJson, readStateText, writeStateText, } from "./shared.js";
|
|
10
10
|
// SPEC §4.19.2: a helper tool proposal names the helper role, the binary
|
|
11
11
|
// and the exact pin from the human's registry lookup: role=command@pin.
|
|
@@ -114,6 +114,25 @@ export async function initPreamble(req) {
|
|
|
114
114
|
}
|
|
115
115
|
return root.value;
|
|
116
116
|
}
|
|
117
|
+
// SPEC §4.13.1: the shared write-then-guard tail of the init path: plan,
|
|
118
|
+
// write, fresh-config guard, baseline guard. init and setup run the same
|
|
119
|
+
// tail; one helper owns it.
|
|
120
|
+
export async function runInitSequence(root, req, flags, planned, engineVersion) {
|
|
121
|
+
const plan = await buildGuardrailPlan(root, flags, planned, engineVersion);
|
|
122
|
+
const wrote = await writePlan(root, plan, engineVersion);
|
|
123
|
+
if (!wrote.ok) {
|
|
124
|
+
return fail("CDX-E903", wrote.error.message, wrote.error.remediation, req.json);
|
|
125
|
+
}
|
|
126
|
+
const fresh = await checkFreshConfig(root, req);
|
|
127
|
+
if (!fresh.ok) {
|
|
128
|
+
return err(fresh.error);
|
|
129
|
+
}
|
|
130
|
+
const baseline = await ensureBaseline(root, req);
|
|
131
|
+
if (!baseline.ok) {
|
|
132
|
+
return fail(baseline.error.code, baseline.error.message, baseline.error.remediation, req.json);
|
|
133
|
+
}
|
|
134
|
+
return ok({ written: wrote.value, baseline: baseline.value });
|
|
135
|
+
}
|
|
117
136
|
// The plan assembly handleInit writes: read-only inputs in, GuardrailPlan
|
|
118
137
|
// out. Writing stays on the command so the plan builder never touches
|
|
119
138
|
// the disk.
|
package/dist/cli/init.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { Result } from "../kernel/index.ts";
|
|
2
|
+
import { type InitFlags } from "./init-plan.ts";
|
|
2
3
|
import type { CommandRequest } from "./shared.ts";
|
|
4
|
+
export declare function parseInitFlags(args: ReadonlyArray<string>): InitFlags | undefined;
|
|
3
5
|
export declare function handleInit(req: CommandRequest): Promise<Result<number>>;
|
package/dist/cli/init.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import { ENGINE_VERSION } from "../features/config/index.js";
|
|
6
6
|
import { isBuiltinPackId } from "../features/packs/index.js";
|
|
7
|
-
import {
|
|
8
|
-
import { KNOWN_HELPER_ROLES
|
|
9
|
-
import {
|
|
7
|
+
import { defaultHarnesses, emitInitResult, initPreamble, reportExisting, runInitSequence, } from "./init-plan.js";
|
|
8
|
+
import { KNOWN_HELPER_ROLES } from "./plan.js";
|
|
9
|
+
import { readStateText, usageError } from "./shared.js";
|
|
10
10
|
const KNOWN_PROFILES = new Set([
|
|
11
11
|
"app",
|
|
12
12
|
"library",
|
|
@@ -55,7 +55,7 @@ function narrowInitTools(specs) {
|
|
|
55
55
|
}
|
|
56
56
|
return tools;
|
|
57
57
|
}
|
|
58
|
-
function parseInitFlags(args) {
|
|
58
|
+
export function parseInitFlags(args) {
|
|
59
59
|
try {
|
|
60
60
|
const parsed = parseArgs({
|
|
61
61
|
args: [...args],
|
|
@@ -105,18 +105,9 @@ export async function handleInit(req) {
|
|
|
105
105
|
if (existing !== undefined && !flags.repair) {
|
|
106
106
|
return reportExisting(req, root);
|
|
107
107
|
}
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return fail("CDX-E903", wrote.error.message, wrote.error.remediation, req.json);
|
|
108
|
+
const sequenced = await runInitSequence(root, req, flags, planned, engineVersion);
|
|
109
|
+
if (!sequenced.ok) {
|
|
110
|
+
return sequenced;
|
|
112
111
|
}
|
|
113
|
-
|
|
114
|
-
if (!fresh.ok) {
|
|
115
|
-
return fresh;
|
|
116
|
-
}
|
|
117
|
-
const baseline = await ensureBaseline(root, req);
|
|
118
|
-
if (!baseline.ok) {
|
|
119
|
-
return fail(baseline.error.code, baseline.error.message, baseline.error.remediation, req.json);
|
|
120
|
-
}
|
|
121
|
-
return emitInitResult(req, root, wrote.value, baseline.value);
|
|
112
|
+
return emitInitResult(req, root, sequenced.value.written, sequenced.value.baseline);
|
|
122
113
|
}
|
package/dist/cli/plan.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export interface GuardrailPlan {
|
|
|
10
10
|
readonly files: ReadonlyArray<{
|
|
11
11
|
path: string;
|
|
12
12
|
content: string;
|
|
13
|
+
owner: string;
|
|
14
|
+
mode: "file";
|
|
13
15
|
}>;
|
|
14
16
|
}
|
|
15
17
|
export interface PlanExtras {
|
|
@@ -18,7 +20,7 @@ export interface PlanExtras {
|
|
|
18
20
|
readonly architecture?: unknown;
|
|
19
21
|
readonly tools?: unknown;
|
|
20
22
|
}
|
|
21
|
-
export declare function guardrailPlan(harnesses: ReadonlyArray<string>, profile: string, repoName: string, existingAgents: string | undefined, extras?: PlanExtras, engineVersion?: string): GuardrailPlan;
|
|
23
|
+
export declare function guardrailPlan(harnesses: ReadonlyArray<string>, profile: string, repoName: string, existingAgents: string | undefined, extras?: PlanExtras, engineVersion?: string, root?: string): GuardrailPlan;
|
|
22
24
|
export declare function writePlan(root: string, plan: GuardrailPlan, engineVersion?: string): Promise<Result<Array<{
|
|
23
25
|
path: string;
|
|
24
26
|
hash: string;
|
package/dist/cli/plan.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPEC §4.14.1 + §4.14.5: guardrail plan builders. The file list (config,
|
|
2
2
|
// steering block, CI workflow, hooks, launcher) and the manifest write are
|
|
3
3
|
// shared by init and regen, so regeneration stays byte-identical.
|
|
4
|
+
import { renderHarnessFiles } from "../features/adapters/index.js";
|
|
4
5
|
import { renderWorkflow } from "../features/ci/index.js";
|
|
5
6
|
import { ENGINE_VERSION } from "../features/config/index.js";
|
|
6
7
|
import { buildManifest, EXPECTED_HOOKS_PATH, HOOK_NAMES, hookInstallPlan, renderHookScript, } from "../features/integrity/index.js";
|
|
@@ -65,30 +66,65 @@ function withManagedBlock(existing) {
|
|
|
65
66
|
const after = end + MANAGED_END.length;
|
|
66
67
|
return `${existing.slice(0, start)}${block}${existing.slice(after)}`;
|
|
67
68
|
}
|
|
68
|
-
export function guardrailPlan(harnesses, profile, repoName, existingAgents, extras = {}, engineVersion = ENGINE_VERSION) {
|
|
69
|
+
export function guardrailPlan(harnesses, profile, repoName, existingAgents, extras = {}, engineVersion = ENGINE_VERSION, root = ".") {
|
|
69
70
|
const files = [];
|
|
70
71
|
files.push({
|
|
71
72
|
path: ".codictator/config.json",
|
|
72
73
|
content: configText(harnesses, profile, extras, engineVersion),
|
|
74
|
+
owner: "codictator",
|
|
75
|
+
mode: "file",
|
|
76
|
+
});
|
|
77
|
+
files.push({
|
|
78
|
+
path: "AGENTS.md",
|
|
79
|
+
content: withManagedBlock(existingAgents),
|
|
80
|
+
owner: "codictator",
|
|
81
|
+
mode: "file",
|
|
73
82
|
});
|
|
74
|
-
files.push({ path: "AGENTS.md", content: withManagedBlock(existingAgents) });
|
|
75
83
|
const workflow = renderWorkflow(repoName);
|
|
76
84
|
files.push({
|
|
77
85
|
path: ".github/workflows/codictator.yml",
|
|
78
86
|
content: workflow.text ?? "",
|
|
87
|
+
owner: "codictator",
|
|
88
|
+
mode: "file",
|
|
79
89
|
});
|
|
80
90
|
for (const hook of HOOK_NAMES) {
|
|
81
91
|
files.push({
|
|
82
92
|
path: `${EXPECTED_HOOKS_PATH}/${hook}`,
|
|
83
93
|
content: renderHookScript(hook),
|
|
94
|
+
owner: "codictator",
|
|
95
|
+
mode: "file",
|
|
84
96
|
});
|
|
85
97
|
}
|
|
86
98
|
const plan = hookInstallPlan({});
|
|
87
99
|
files.push({
|
|
88
100
|
path: `${EXPECTED_HOOKS_PATH}/.gitattributes`,
|
|
89
101
|
content: plan.gitattributes,
|
|
102
|
+
owner: "codictator",
|
|
103
|
+
mode: "file",
|
|
104
|
+
});
|
|
105
|
+
files.push({
|
|
106
|
+
path: ".codictator/bin/run.mjs",
|
|
107
|
+
content: LAUNCHER_TEXT,
|
|
108
|
+
owner: "codictator",
|
|
109
|
+
mode: "file",
|
|
90
110
|
});
|
|
91
|
-
|
|
111
|
+
// SPEC §4.13.1 step 2 + §4.5: every configured built-in harness owns
|
|
112
|
+
// its rendered files; universal renders nothing, custom:* stays with
|
|
113
|
+
// renderCustomHarnessFiles. Renders ignore the input, so the "." default
|
|
114
|
+
// keeps the init path working unchanged. SPEC §4.6.2 + core/guardrail-manifest:
|
|
115
|
+
// one row per path, first occurrence wins: base files go first so the
|
|
116
|
+
// plan's managed AGENTS.md beats any harness-rendered AGENTS.md, then
|
|
117
|
+
// harness files follow in sorted harness-id order, skipping paths
|
|
118
|
+
// already emitted (cursor/gemini-cli AGENTS.md, codex-cli/muse-code
|
|
119
|
+
// .agents/skills overlap), so writePlan writes each path once.
|
|
120
|
+
const seen = new Set(files.map((entry) => entry.path));
|
|
121
|
+
const sortedHarnesses = [...new Set(harnesses)].sort();
|
|
122
|
+
for (const artifact of renderHarnessFiles(sortedHarnesses, root)) {
|
|
123
|
+
if (!seen.has(artifact.path)) {
|
|
124
|
+
seen.add(artifact.path);
|
|
125
|
+
files.push({ ...artifact, mode: "file" });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
92
128
|
return { files };
|
|
93
129
|
}
|
|
94
130
|
export async function writePlan(root, plan, engineVersion = ENGINE_VERSION) {
|
|
@@ -107,12 +143,15 @@ export async function writePlan(root, plan, engineVersion = ENGINE_VERSION) {
|
|
|
107
143
|
hash: sha256Text(normalizeText(file.content)),
|
|
108
144
|
});
|
|
109
145
|
}
|
|
110
|
-
const manifest = buildManifest(engineVersion, written.map((entry) =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
146
|
+
const manifest = buildManifest(engineVersion, written.map((entry, index) => {
|
|
147
|
+
const planned = plan.files[index];
|
|
148
|
+
return {
|
|
149
|
+
path: entry.path,
|
|
150
|
+
owner: planned === undefined ? "codictator" : planned.owner,
|
|
151
|
+
mode: planned === undefined ? "file" : planned.mode,
|
|
152
|
+
hash: entry.hash,
|
|
153
|
+
};
|
|
154
|
+
}));
|
|
116
155
|
const saved = await ports.value.fs.write(".codictator/manifest.json", encode(`${stableStringify(manifest)}\n`));
|
|
117
156
|
if (!saved.ok) {
|
|
118
157
|
return err(saved.error);
|
package/dist/cli/serve-run.js
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Result } from "../kernel/index.ts";
|
|
2
|
+
import type { SetupStep } from "./setup.ts";
|
|
3
|
+
import type { CommandRequest } from "./shared.ts";
|
|
4
|
+
export declare function ensureGitignoreEntries(root: string): Promise<Result<{
|
|
5
|
+
added: Array<string>;
|
|
6
|
+
}>>;
|
|
7
|
+
export declare function setupGitignoreStep(req: CommandRequest, root: string, steps: Array<SetupStep>): Promise<Result<number>>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { err, ok } from "../kernel/index.js";
|
|
2
|
+
import { fail, printHuman, readStateText, writeStateText } from "./shared.js";
|
|
3
|
+
const SETUP_GITIGNORE_ENTRIES = [
|
|
4
|
+
".codictator/state/",
|
|
5
|
+
".codictator/cache/",
|
|
6
|
+
".codictator/tool/node_modules/",
|
|
7
|
+
];
|
|
8
|
+
export async function ensureGitignoreEntries(root) {
|
|
9
|
+
const existing = await readStateText(root, ".gitignore");
|
|
10
|
+
const present = new Set((existing ?? "").split("\n").map((line) => line.trim()));
|
|
11
|
+
const missing = SETUP_GITIGNORE_ENTRIES.filter((entry) => !present.has(entry));
|
|
12
|
+
if (missing.length === 0) {
|
|
13
|
+
return ok({ added: [] });
|
|
14
|
+
}
|
|
15
|
+
const base = existing === undefined || existing === ""
|
|
16
|
+
? ""
|
|
17
|
+
: existing.endsWith("\n")
|
|
18
|
+
? existing
|
|
19
|
+
: `${existing}\n`;
|
|
20
|
+
const saved = await writeStateText(root, ".gitignore", `${base}${missing.join("\n")}\n`);
|
|
21
|
+
if (!saved.ok) {
|
|
22
|
+
return err(saved.error);
|
|
23
|
+
}
|
|
24
|
+
return ok({ added: [...missing] });
|
|
25
|
+
}
|
|
26
|
+
// SPEC §4.19.6 step 3: guarantee the manifest-owned .gitignore entries.
|
|
27
|
+
export async function setupGitignoreStep(req, root, steps) {
|
|
28
|
+
const ensured = await ensureGitignoreEntries(root);
|
|
29
|
+
if (!ensured.ok) {
|
|
30
|
+
return fail("CDX-E903", ensured.error.message, ensured.error.remediation, req.json);
|
|
31
|
+
}
|
|
32
|
+
steps.push({
|
|
33
|
+
step: "gitignore",
|
|
34
|
+
ok: true,
|
|
35
|
+
detail: { added: ensured.value.added },
|
|
36
|
+
});
|
|
37
|
+
if (ensured.value.added.length > 0) {
|
|
38
|
+
printHuman(`setup: added ${String(ensured.value.added.length)} .gitignore entries`, req.quiet);
|
|
39
|
+
}
|
|
40
|
+
return ok(0);
|
|
41
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { InitFlags } from "./init-plan.ts";
|
|
2
|
+
export declare const SETUP_EXTENSION_ID = "codictator.codictator-vscode";
|
|
3
|
+
export declare const SETUP_EXTENSION_INSTALL = "code --install-extension codictator.codictator-vscode";
|
|
4
|
+
export declare const SETUP_PACK_REPAIR = "npx codictator setup --repair --pack typescript";
|
|
5
|
+
export declare const SETUP_PACK_FRESH = "npx codictator setup --pack typescript";
|
|
6
|
+
export declare function setupHandoffLines(emptyPacks: boolean): Array<string>;
|
|
7
|
+
export declare function hasEffectivePacks(root: string, flags: InitFlags): Promise<boolean>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// SPEC §4.19.6 step 8: pack-less handoff text. Split from setup.ts
|
|
2
|
+
// (orchestration) by responsibility so setup.ts stays below the
|
|
3
|
+
// core/file-size 300 cap; the effective pack list mirrors the plan data.
|
|
4
|
+
import { parseJson } from "../kernel/index.js";
|
|
5
|
+
import { readStateText } from "./shared.js";
|
|
6
|
+
// The extension empty-state lives on the vscode branch (§4.20.1);
|
|
7
|
+
// handoff here only prints the install command.
|
|
8
|
+
export const SETUP_EXTENSION_ID = "codictator.codictator-vscode";
|
|
9
|
+
export const SETUP_EXTENSION_INSTALL = "code --install-extension codictator.codictator-vscode";
|
|
10
|
+
export const SETUP_PACK_REPAIR = "npx codictator setup --repair --pack typescript";
|
|
11
|
+
export const SETUP_PACK_FRESH = "npx codictator setup --pack typescript";
|
|
12
|
+
export function setupHandoffLines(emptyPacks) {
|
|
13
|
+
if (!emptyPacks) {
|
|
14
|
+
return [
|
|
15
|
+
"setup complete: open this folder in VS Code to see the Codictator views",
|
|
16
|
+
`extension: ${SETUP_EXTENSION_INSTALL}`,
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
return [
|
|
20
|
+
"Setup complete — core rules only. No language packs installed.",
|
|
21
|
+
"To add a language pack, run exactly one line and press Enter:",
|
|
22
|
+
` ${SETUP_PACK_REPAIR}`,
|
|
23
|
+
"To install the VS Code extension, run:",
|
|
24
|
+
` ${SETUP_EXTENSION_INSTALL}`,
|
|
25
|
+
`Fresh adoption omits --repair: ${SETUP_PACK_FRESH}`,
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
// SPEC §4.19.6 step 8: the effective pack list mirrors the plan data:
|
|
29
|
+
// explicit --pack flags win, otherwise the written config owns it.
|
|
30
|
+
export async function hasEffectivePacks(root, flags) {
|
|
31
|
+
if (flags.packs.length > 0) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
const text = await readStateText(root, ".codictator/config.json");
|
|
35
|
+
if (text === undefined) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const parsed = parseJson(text);
|
|
39
|
+
if (!parsed.ok || typeof parsed.value !== "object" || parsed.value === null) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const packs = parsed.value.packs;
|
|
43
|
+
return Array.isArray(packs) && packs.length > 0;
|
|
44
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Result } from "../kernel/index.ts";
|
|
2
|
+
import type { SetupStep } from "./setup.ts";
|
|
3
|
+
import type { CommandRequest } from "./shared.ts";
|
|
4
|
+
export interface SetupInstallTarget {
|
|
5
|
+
readonly dir: string;
|
|
6
|
+
readonly lockRel: string;
|
|
7
|
+
readonly dirRel: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function setupInstallLayout(req: CommandRequest, root: string, steps: Array<SetupStep>): Promise<Result<SetupInstallTarget>>;
|
|
10
|
+
export declare function setupNpmArgv(platform?: string, execPath?: string): ReadonlyArray<string>;
|
|
11
|
+
export declare function setupInstallHere(req: CommandRequest, root: string, target: SetupInstallTarget, steps: Array<SetupStep>): Promise<Result<number>>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// SPEC §4.19.4 + §4.19.6 steps 4-5: the engine install layout and the
|
|
2
|
+
// clean install here. npm repos install at the root; other repos get
|
|
3
|
+
// .codictator/tool/package.json pinning the engine exactly, then npm ci
|
|
4
|
+
// runs in the directory owning the lockfile. A registry or network outage
|
|
5
|
+
// maps to CDX-E401 (exit 5); any other install failure is a missing
|
|
6
|
+
// engine, CDX-E003 (exit 6).
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { ENGINE_VERSION } from "../features/config/index.js";
|
|
9
|
+
import { err, ok, stableStringify } from "../kernel/index.js";
|
|
10
|
+
import { fail, portsFor, printHuman, readStateText, writeStateText, } from "./shared.js";
|
|
11
|
+
export async function setupInstallLayout(req, root, steps) {
|
|
12
|
+
const rootManifest = await readStateText(root, "package.json");
|
|
13
|
+
if (rootManifest !== undefined) {
|
|
14
|
+
steps.push({ step: "install", ok: true, detail: { dir: "." } });
|
|
15
|
+
return ok({ dir: root, lockRel: "package-lock.json", dirRel: "." });
|
|
16
|
+
}
|
|
17
|
+
const toolManifest = await readStateText(root, ".codictator/tool/package.json");
|
|
18
|
+
if (toolManifest === undefined) {
|
|
19
|
+
const engineVersion = req.engineVersion ?? ENGINE_VERSION;
|
|
20
|
+
const saved = await writeStateText(root, ".codictator/tool/package.json", `${stableStringify({
|
|
21
|
+
name: "codictator-tool",
|
|
22
|
+
private: true,
|
|
23
|
+
dependencies: { codictator: engineVersion },
|
|
24
|
+
})}\n`);
|
|
25
|
+
if (!saved.ok) {
|
|
26
|
+
return err(saved.error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
steps.push({
|
|
30
|
+
step: "install",
|
|
31
|
+
ok: true,
|
|
32
|
+
detail: { dir: ".codictator/tool" },
|
|
33
|
+
});
|
|
34
|
+
return ok({
|
|
35
|
+
dir: `${root}/.codictator/tool`,
|
|
36
|
+
lockRel: ".codictator/tool/package-lock.json",
|
|
37
|
+
dirRel: ".codictator/tool",
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function looksLikeRegistryOutage(output) {
|
|
41
|
+
return /ENOTFOUND|EAI_AGAIN|ETIMEDOUT|ECONNRESET|ECONNREFUSED|registry/i.test(output);
|
|
42
|
+
}
|
|
43
|
+
// SPEC §4.1.1: the child spawns with shell:false always, so on win32 the
|
|
44
|
+
// bare `npm` shim never resolves: it is an npm.cmd batch file, which the
|
|
45
|
+
// shell-less spawn cannot execute (ENOENT without, EINVAL with, the
|
|
46
|
+
// extension). The fix stays shell-free by exec'ing the real entry beside
|
|
47
|
+
// the running node: node <node-dir>/node_modules/npm/bin/npm-cli.js ci.
|
|
48
|
+
// POSIX keeps `npm` from PATH. Pure in platform and execPath so tests pin
|
|
49
|
+
// the exact proc argv without spawning anything.
|
|
50
|
+
export function setupNpmArgv(platform = process.platform, execPath = process.execPath) {
|
|
51
|
+
if (platform !== "win32") {
|
|
52
|
+
return ["npm", "ci"];
|
|
53
|
+
}
|
|
54
|
+
return [
|
|
55
|
+
execPath,
|
|
56
|
+
path.join(path.dirname(execPath), "node_modules", "npm", "bin", "npm-cli.js"),
|
|
57
|
+
"ci",
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
export async function setupInstallHere(req, root, target, steps) {
|
|
61
|
+
const lock = await readStateText(root, target.lockRel);
|
|
62
|
+
if (lock === undefined) {
|
|
63
|
+
return fail("CDX-E003", `no lockfile at ${target.lockRel}, so npm ci cannot run`, `Run npm install in ${target.dirRel} (needs network access), then rerun codictator setup.`, req.json);
|
|
64
|
+
}
|
|
65
|
+
const ports = await portsFor(root);
|
|
66
|
+
if (!ports.ok) {
|
|
67
|
+
return fail("CDX-E903", ports.error.message, ports.error.remediation, req.json);
|
|
68
|
+
}
|
|
69
|
+
const ran = await ports.value.proc.run(setupNpmArgv(), {
|
|
70
|
+
cwd: target.dir,
|
|
71
|
+
env: {},
|
|
72
|
+
timeoutMs: 300000,
|
|
73
|
+
maxOutputBytes: 4 * 1024 * 1024,
|
|
74
|
+
});
|
|
75
|
+
if (!ran.timedOut && !ran.truncated && ran.code === 0) {
|
|
76
|
+
steps.push({ step: "install-here", ok: true });
|
|
77
|
+
printHuman(`setup: npm ci passed in ${target.dirRel}`, req.quiet);
|
|
78
|
+
return ok(0);
|
|
79
|
+
}
|
|
80
|
+
if (ran.timedOut || looksLikeRegistryOutage(`${ran.stdout}\n${ran.stderr}`)) {
|
|
81
|
+
return fail("CDX-E401", "registry or network unavailable during install", "Retry with network access, then rerun codictator setup.", req.json);
|
|
82
|
+
}
|
|
83
|
+
return fail("CDX-E003", `engine install failed in ${target.dirRel} (npm ci exit ${String(ran.code)})`, `Run npm ci in ${target.dirRel}, then rerun codictator setup.`, req.json);
|
|
84
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Result } from "../kernel/index.ts";
|
|
2
|
+
import type { CommandRequest } from "./shared.ts";
|
|
3
|
+
export declare const SETUP_USAGE = "usage: codictator setup [--harness <id> ...] [--profile <app|library|cli|service>] [--pack <id> ...] [--tool <role>=<command>@<pin> ...] [--repair]";
|
|
4
|
+
export interface SetupStep {
|
|
5
|
+
readonly step: string;
|
|
6
|
+
readonly ok: boolean;
|
|
7
|
+
readonly detail?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export declare function handleSetup(req: CommandRequest): Promise<Result<number>>;
|