ai-policy-pack-cli 0.1.0 → 0.1.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/src/cli.js +10 -4
- package/dist/src/commands/install.js +1 -1
- package/dist/src/commands/update.js +1 -1
- package/dist/src/commands/verify.js +1 -1
- package/dist/src/core/load-policy.js +23 -4
- package/dist/test/adapters.spec.js +1 -1
- package/dist/test/install-verify-update.spec.js +5 -0
- package/dist/test/npx-runtime.spec.js +29 -0
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { promises as fs } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { parseArgs } from "./core/args.js";
|
|
5
6
|
import { logger } from "./core/logger.js";
|
|
6
7
|
import { CliError } from "./core/errors.js";
|
|
@@ -9,17 +10,18 @@ import { runVerifyCommand } from "./commands/verify.js";
|
|
|
9
10
|
import { runUpdateCommand } from "./commands/update.js";
|
|
10
11
|
async function main() {
|
|
11
12
|
const cwd = process.cwd();
|
|
12
|
-
const
|
|
13
|
+
const packageRoot = resolvePackageRoot();
|
|
14
|
+
const packageVersion = await readPackageVersion(packageRoot);
|
|
13
15
|
const args = parseArgs(process.argv.slice(2));
|
|
14
16
|
switch (args.command) {
|
|
15
17
|
case "install":
|
|
16
|
-
await runInstallCommand(args, { cwd, packageVersion, logger });
|
|
18
|
+
await runInstallCommand(args, { cwd, packageRoot, packageVersion, logger });
|
|
17
19
|
return;
|
|
18
20
|
case "verify":
|
|
19
|
-
await runVerifyCommand(args, { cwd, logger });
|
|
21
|
+
await runVerifyCommand(args, { cwd, packageRoot, logger });
|
|
20
22
|
return;
|
|
21
23
|
case "update":
|
|
22
|
-
await runUpdateCommand(args, { cwd, packageVersion, logger });
|
|
24
|
+
await runUpdateCommand(args, { cwd, packageRoot, packageVersion, logger });
|
|
23
25
|
return;
|
|
24
26
|
default:
|
|
25
27
|
throw new CliError(`Unsupported command: ${String(args.command)}`);
|
|
@@ -30,6 +32,10 @@ async function readPackageVersion(cwd) {
|
|
|
30
32
|
const parsed = JSON.parse(await fs.readFile(packagePath, "utf8"));
|
|
31
33
|
return parsed.version ?? "0.0.0";
|
|
32
34
|
}
|
|
35
|
+
function resolvePackageRoot() {
|
|
36
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
37
|
+
return path.resolve(path.dirname(currentFile), "..", "..");
|
|
38
|
+
}
|
|
33
39
|
main().catch((error) => {
|
|
34
40
|
if (error instanceof CliError) {
|
|
35
41
|
logger.error(error.message);
|
|
@@ -7,7 +7,7 @@ import { CliError } from "../core/errors.js";
|
|
|
7
7
|
export async function runInstallCommand(args, deps) {
|
|
8
8
|
const targets = await resolveTargets(args);
|
|
9
9
|
const presetName = args.preset ?? "karpathy";
|
|
10
|
-
const policy = await loadPolicyPreset(deps.cwd, presetName);
|
|
10
|
+
const policy = await loadPolicyPreset(deps.cwd, presetName, deps.packageRoot);
|
|
11
11
|
const files = renderForTargets(policy, targets);
|
|
12
12
|
deps.logger.info(`Preset: ${policy.name}`);
|
|
13
13
|
deps.logger.info(`Targets: ${targets.join(", ")}`);
|
|
@@ -10,7 +10,7 @@ export async function runUpdateCommand(args, deps) {
|
|
|
10
10
|
throw new CliError("Manifest not found. Run install first.");
|
|
11
11
|
}
|
|
12
12
|
const presetName = args.preset || manifest.preset;
|
|
13
|
-
const preset = await loadPolicyPreset(deps.cwd, presetName);
|
|
13
|
+
const preset = await loadPolicyPreset(deps.cwd, presetName, deps.packageRoot);
|
|
14
14
|
const files = renderForTargets(preset, manifest.targets);
|
|
15
15
|
deps.logger.info(`Updating preset: ${preset.name}`);
|
|
16
16
|
for (const file of files) {
|
|
@@ -9,7 +9,7 @@ export async function runVerifyCommand(args, deps) {
|
|
|
9
9
|
if (!manifest) {
|
|
10
10
|
throw new CliError("Manifest not found. Run install first.");
|
|
11
11
|
}
|
|
12
|
-
const preset = await loadPolicyPreset(deps.cwd, manifest.preset);
|
|
12
|
+
const preset = await loadPolicyPreset(deps.cwd, manifest.preset, deps.packageRoot);
|
|
13
13
|
const rendered = renderForTargets(preset, manifest.targets);
|
|
14
14
|
const renderedMap = new Map(rendered.map((file) => [file.path, file.content]));
|
|
15
15
|
const mismatches = [];
|
|
@@ -2,9 +2,28 @@ import path from "node:path";
|
|
|
2
2
|
import { promises as fs } from "node:fs";
|
|
3
3
|
import yaml from "js-yaml";
|
|
4
4
|
import { validatePolicyPreset } from "./policy-schema.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
5
|
+
import { CliError } from "./errors.js";
|
|
6
|
+
export async function loadPolicyPreset(projectCwd, presetName, packageRoot) {
|
|
7
|
+
const projectPresetPath = path.join(projectCwd, "presets", presetName, "policy.yml");
|
|
8
|
+
const packagePresetPath = path.join(packageRoot, "presets", presetName, "policy.yml");
|
|
9
|
+
const source = await readFirstExisting([projectPresetPath, packagePresetPath]);
|
|
10
|
+
if (!source) {
|
|
11
|
+
throw new CliError(`Preset '${presetName}' not found. Checked: ${projectPresetPath} and ${packagePresetPath}`);
|
|
12
|
+
}
|
|
13
|
+
const parsed = yaml.load(source);
|
|
9
14
|
return validatePolicyPreset(parsed);
|
|
10
15
|
}
|
|
16
|
+
async function readFirstExisting(paths) {
|
|
17
|
+
for (const filePath of paths) {
|
|
18
|
+
try {
|
|
19
|
+
return await fs.readFile(filePath, "utf8");
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error.code === "ENOENT") {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
@@ -4,7 +4,7 @@ import { renderForTargets } from "../src/core/render.js";
|
|
|
4
4
|
const CWD = "d:/ATS/Project/AI/rulecusor";
|
|
5
5
|
describe("adapters render", () => {
|
|
6
6
|
it("renders target-specific files", async () => {
|
|
7
|
-
const policy = await loadPolicyPreset(CWD, "karpathy");
|
|
7
|
+
const policy = await loadPolicyPreset(CWD, "karpathy", CWD);
|
|
8
8
|
const files = renderForTargets(policy, ["cursor", "copilot", "antigravity", "claude"]);
|
|
9
9
|
const paths = files.map((item) => item.path).sort();
|
|
10
10
|
expect(paths).toContain(".cursor/rules/global-policy.mdc");
|
|
@@ -18,25 +18,30 @@ describe("install -> verify -> drift -> update flow", () => {
|
|
|
18
18
|
await copyFixtureSourceToWorkspace(root, workspace);
|
|
19
19
|
await runInstallCommand(parseArgs(["install", "--targets", "cursor,copilot,antigravity,claude", "--yes"]), {
|
|
20
20
|
cwd: workspace,
|
|
21
|
+
packageRoot: workspace,
|
|
21
22
|
packageVersion: "0.1.0",
|
|
22
23
|
logger: silentLogger
|
|
23
24
|
});
|
|
24
25
|
await runVerifyCommand(parseArgs(["verify"]), {
|
|
25
26
|
cwd: workspace,
|
|
27
|
+
packageRoot: workspace,
|
|
26
28
|
logger: silentLogger
|
|
27
29
|
});
|
|
28
30
|
await fs.appendFile(path.join(workspace, "AGENTS.md"), "\nmanual drift\n", "utf8");
|
|
29
31
|
await expect(runVerifyCommand(parseArgs(["verify"]), {
|
|
30
32
|
cwd: workspace,
|
|
33
|
+
packageRoot: workspace,
|
|
31
34
|
logger: silentLogger
|
|
32
35
|
})).rejects.toThrow();
|
|
33
36
|
await runUpdateCommand(parseArgs(["update", "--yes"]), {
|
|
34
37
|
cwd: workspace,
|
|
38
|
+
packageRoot: workspace,
|
|
35
39
|
packageVersion: "0.1.1",
|
|
36
40
|
logger: silentLogger
|
|
37
41
|
});
|
|
38
42
|
await runVerifyCommand(parseArgs(["verify"]), {
|
|
39
43
|
cwd: workspace,
|
|
44
|
+
packageRoot: workspace,
|
|
40
45
|
logger: silentLogger
|
|
41
46
|
});
|
|
42
47
|
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { runInstallCommand } from "../src/commands/install.js";
|
|
5
|
+
import { parseArgs } from "../src/core/args.js";
|
|
6
|
+
import { createTempWorkspace, copyFixtureSourceToWorkspace } from "./helpers.js";
|
|
7
|
+
const silentLogger = {
|
|
8
|
+
info: (_message) => undefined,
|
|
9
|
+
warn: (_message) => undefined,
|
|
10
|
+
error: (_message) => undefined
|
|
11
|
+
};
|
|
12
|
+
describe("runtime path behavior", () => {
|
|
13
|
+
it("installs into workspace without local package.json or presets", async () => {
|
|
14
|
+
const sourceRoot = "d:/ATS/Project/AI/rulecusor";
|
|
15
|
+
const packageRoot = await createTempWorkspace("policy-pack-runtime");
|
|
16
|
+
const projectWorkspace = await createTempWorkspace("policy-pack-project");
|
|
17
|
+
// Simulate npm package root with presets available.
|
|
18
|
+
await copyFixtureSourceToWorkspace(sourceRoot, packageRoot);
|
|
19
|
+
await fs.rm(path.join(projectWorkspace, "presets"), { recursive: true, force: true });
|
|
20
|
+
await fs.rm(path.join(projectWorkspace, "package.json"), { force: true });
|
|
21
|
+
await runInstallCommand(parseArgs(["install", "--targets", "cursor", "--yes"]), {
|
|
22
|
+
cwd: projectWorkspace,
|
|
23
|
+
packageRoot,
|
|
24
|
+
packageVersion: "0.1.0",
|
|
25
|
+
logger: silentLogger
|
|
26
|
+
});
|
|
27
|
+
await expect(fs.readFile(path.join(projectWorkspace, ".cursor/rules/global-policy.mdc"), "utf8")).resolves.toContain("Team Global Rules");
|
|
28
|
+
});
|
|
29
|
+
});
|