@kuznai/inception-engine 0.18.0 → 0.20.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/README.md +72 -43
- package/dist/{config → src/config}/agents.js +174 -24
- package/dist/src/core/adapters/agent-definitions.d.ts +8 -0
- package/dist/src/core/adapters/agent-definitions.js +162 -0
- package/dist/{core → src/core}/adapters/frontmatter.d.ts +10 -0
- package/dist/{core → src/core}/adapters/frontmatter.js +23 -6
- package/dist/{core → src/core}/adapters/index.d.ts +1 -1
- package/dist/{core → src/core}/adapters/index.js +4 -4
- package/dist/{core → src/core}/adapters/mcp.d.ts +2 -2
- package/dist/{core → src/core}/adapters/mcp.js +21 -24
- package/dist/{core → src/core}/adapters/permissions.js +16 -20
- package/dist/{core → src/core}/adapters/rules.d.ts +2 -2
- package/dist/src/core/adapters/rules.js +126 -0
- package/dist/src/core/capabilities.d.ts +47 -0
- package/dist/src/core/capabilities.js +280 -0
- package/dist/{core → src/core}/deploy.d.ts +1 -1
- package/dist/{core → src/core}/deploy.js +273 -92
- package/dist/{core → src/core}/init.js +126 -52
- package/dist/src/core/merge-patch.d.ts +4 -0
- package/dist/src/core/merge-patch.js +46 -0
- package/dist/{core → src/core}/ownership.d.ts +2 -2
- package/dist/{core → src/core}/ownership.js +13 -0
- package/dist/{core → src/core}/preflight.d.ts +1 -1
- package/dist/src/core/preflight.js +280 -0
- package/dist/{core → src/core}/resolve.d.ts +1 -1
- package/dist/{core → src/core}/resolve.js +7 -4
- package/dist/{core → src/core}/revert.js +95 -33
- package/dist/{core → src/core}/runtime-paths.d.ts +2 -1
- package/dist/{core → src/core}/runtime-paths.js +30 -23
- package/dist/{core → src/core}/validation.d.ts +10 -1
- package/dist/{core → src/core}/validation.js +75 -0
- package/dist/src/formatters.d.ts +5 -0
- package/dist/src/formatters.js +58 -0
- package/dist/{index.js → src/index.js} +8 -24
- package/dist/src/logger.js +62 -0
- package/dist/{schemas → src/schemas}/manifest.d.ts +22 -0
- package/dist/{schemas → src/schemas}/manifest.js +19 -5
- package/dist/{schemas → src/schemas}/registry.d.ts +74 -0
- package/dist/{schemas → src/schemas}/registry.js +18 -3
- package/dist/{types.d.ts → src/types.d.ts} +60 -0
- package/dist/test/helpers/fs.d.ts +2 -0
- package/dist/test/helpers/fs.js +17 -0
- package/dist/test/helpers/path.d.ts +10 -0
- package/dist/test/helpers/path.js +15 -0
- package/dist/test/helpers/skill-dir-scenarios.d.ts +7 -0
- package/dist/test/helpers/skill-dir-scenarios.js +206 -0
- package/dist/test/helpers/skill-dir.d.ts +7 -0
- package/dist/test/helpers/skill-dir.js +46 -0
- package/dist/test/os/cross-platform/cross-platform.test.d.ts +1 -0
- package/dist/test/os/cross-platform/cross-platform.test.js +328 -0
- package/dist/test/os/posix/skill-dir.test.d.ts +1 -0
- package/dist/test/os/posix/skill-dir.test.js +19 -0
- package/dist/test/os/windows/revert-integration.test.d.ts +1 -0
- package/dist/test/os/windows/revert-integration.test.js +72 -0
- package/dist/test/os/windows/skill-dir.test.d.ts +1 -0
- package/dist/test/os/windows/skill-dir.test.js +19 -0
- package/dist/test/unit/adapters.test.d.ts +1 -0
- package/dist/test/unit/adapters.test.js +947 -0
- package/dist/test/unit/capabilities.test.d.ts +1 -0
- package/dist/test/unit/capabilities.test.js +71 -0
- package/dist/test/unit/cli.test.d.ts +1 -0
- package/dist/test/unit/cli.test.js +834 -0
- package/dist/test/unit/deploy.test.d.ts +1 -0
- package/dist/test/unit/deploy.test.js +2481 -0
- package/dist/test/unit/detect.test.d.ts +1 -0
- package/dist/test/unit/detect.test.js +192 -0
- package/dist/test/unit/enterprise.test.d.ts +1 -0
- package/dist/test/unit/enterprise.test.js +106 -0
- package/dist/test/unit/frontmatter.test.d.ts +1 -0
- package/dist/test/unit/frontmatter.test.js +102 -0
- package/dist/test/unit/gemini-north-star.test.d.ts +1 -0
- package/dist/test/unit/gemini-north-star.test.js +243 -0
- package/dist/test/unit/manifest.test.d.ts +1 -0
- package/dist/test/unit/manifest.test.js +737 -0
- package/dist/test/unit/migration-golden.test.d.ts +1 -0
- package/dist/test/unit/migration-golden.test.js +197 -0
- package/dist/test/unit/ownership.test.d.ts +1 -0
- package/dist/test/unit/ownership.test.js +587 -0
- package/dist/test/unit/preflight.test.d.ts +1 -0
- package/dist/test/unit/preflight.test.js +501 -0
- package/dist/test/unit/resolve.test.d.ts +1 -0
- package/dist/test/unit/resolve.test.js +119 -0
- package/dist/test/unit/revert.test.d.ts +1 -0
- package/dist/test/unit/revert.test.js +1004 -0
- package/dist/test/unit/toml.test.d.ts +1 -0
- package/dist/test/unit/toml.test.js +55 -0
- package/package.json +1 -1
- package/dist/core/adapters/agent-definitions.d.ts +0 -20
- package/dist/core/adapters/agent-definitions.js +0 -90
- package/dist/core/adapters/rules.js +0 -103
- package/dist/core/preflight.js +0 -98
- package/dist/logger.js +0 -61
- /package/dist/{config → src/config}/agents.d.ts +0 -0
- /package/dist/{config → src/config}/manifest.d.ts +0 -0
- /package/dist/{config → src/config}/manifest.js +0 -0
- /package/dist/{core → src/core}/adapters/permissions.d.ts +0 -0
- /package/dist/{core → src/core}/adapters/toml.d.ts +0 -0
- /package/dist/{core → src/core}/adapters/toml.js +0 -0
- /package/dist/{core → src/core}/detect.d.ts +0 -0
- /package/dist/{core → src/core}/detect.js +0 -0
- /package/dist/{core → src/core}/init.d.ts +0 -0
- /package/dist/{core → src/core}/revert.d.ts +0 -0
- /package/dist/{errors.d.ts → src/errors.d.ts} +0 -0
- /package/dist/{errors.js → src/errors.js} +0 -0
- /package/dist/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/{logger.d.ts → src/logger.d.ts} +0 -0
- /package/dist/{schemas → src/schemas}/errors.d.ts +0 -0
- /package/dist/{schemas → src/schemas}/errors.js +0 -0
- /package/dist/{types.js → src/types.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
6
|
+
import { applyTomlMcpPatch, revertTomlMcpPatch, } from "../../src/core/adapters/toml.js";
|
|
7
|
+
describe("TOML Adapter", () => {
|
|
8
|
+
let tmpDir;
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "inception-toml-test-"));
|
|
11
|
+
});
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
14
|
+
});
|
|
15
|
+
it("applies an MCP server patch to a new TOML file", async () => {
|
|
16
|
+
const target = path.join(tmpDir, "config.toml");
|
|
17
|
+
const config = { command: "npx", args: ["serve"] };
|
|
18
|
+
await applyTomlMcpPatch(target, "my-skill", config);
|
|
19
|
+
const content = await fs.readFile(target, "utf-8");
|
|
20
|
+
assert.match(content, /\[mcpServers\.my-skill\]/);
|
|
21
|
+
assert.match(content, /command = "npx"/);
|
|
22
|
+
assert.match(content, /args = \[ "serve" \]/);
|
|
23
|
+
});
|
|
24
|
+
it("applies an MCP server patch to an existing TOML file preserving other keys", async () => {
|
|
25
|
+
const target = path.join(tmpDir, "config.toml");
|
|
26
|
+
await fs.writeFile(target, 'title = "My App"\n\n[mcpServers.other]\ncommand = "ls"\n');
|
|
27
|
+
const config = { command: "npx", args: ["serve"] };
|
|
28
|
+
await applyTomlMcpPatch(target, "my-skill", config);
|
|
29
|
+
const content = await fs.readFile(target, "utf-8");
|
|
30
|
+
assert.match(content, /title = "My App"/);
|
|
31
|
+
assert.match(content, /\[mcpServers\.other\]/);
|
|
32
|
+
assert.match(content, /\[mcpServers\.my-skill\]/);
|
|
33
|
+
assert.match(content, /command = "npx"/);
|
|
34
|
+
});
|
|
35
|
+
it("reverts an MCP server patch", async () => {
|
|
36
|
+
const target = path.join(tmpDir, "config.toml");
|
|
37
|
+
await fs.writeFile(target, '[mcpServers.other]\ncommand = "ls"\n\n[mcpServers.my-skill]\ncommand = "npx"\n');
|
|
38
|
+
await revertTomlMcpPatch(target, "my-skill");
|
|
39
|
+
const content = await fs.readFile(target, "utf-8");
|
|
40
|
+
assert.match(content, /\[mcpServers\.other\]/);
|
|
41
|
+
assert.doesNotMatch(content, /my-skill/);
|
|
42
|
+
});
|
|
43
|
+
it("cleans up [mcpServers] table if it becomes empty after revert", async () => {
|
|
44
|
+
const target = path.join(tmpDir, "config.toml");
|
|
45
|
+
await fs.writeFile(target, '[mcpServers.my-skill]\ncommand = "npx"\n');
|
|
46
|
+
await revertTomlMcpPatch(target, "my-skill");
|
|
47
|
+
const content = await fs.readFile(target, "utf-8");
|
|
48
|
+
assert.strictEqual(content.trim(), "");
|
|
49
|
+
});
|
|
50
|
+
it("throws error if config.toml is invalid TOML", async () => {
|
|
51
|
+
const target = path.join(tmpDir, "config.toml");
|
|
52
|
+
await fs.writeFile(target, 'this is not = toml "bad"');
|
|
53
|
+
await assert.rejects(() => applyTomlMcpPatch(target, "any", {}), /Invalid TOML document/);
|
|
54
|
+
});
|
|
55
|
+
});
|
package/package.json
CHANGED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { AgentDefinitionEntry } from "../../schemas/manifest.ts";
|
|
2
|
-
import type { AgentId, FileWriteDeployAction, FileWriteRevertAction, PlanWarning } from "../../types.ts";
|
|
3
|
-
export interface AgentDefinitionsAdapterResult {
|
|
4
|
-
actions: FileWriteDeployAction[];
|
|
5
|
-
warnings: PlanWarning[];
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Compiles deploy actions for agentDefinitions manifest entries.
|
|
9
|
-
*
|
|
10
|
-
* Agent definition files are Markdown files (typically with YAML frontmatter)
|
|
11
|
-
* that define custom agents or subagents for a given platform. Unlike
|
|
12
|
-
* agentRules (which target a single shared global file per agent), each
|
|
13
|
-
* definition entry produces a separate file named after the entry in the
|
|
14
|
-
* agent's dedicated agent-definitions directory (repo-local).
|
|
15
|
-
*
|
|
16
|
-
* Target paths use the `{repo}` placeholder so definitions land in the
|
|
17
|
-
* repository being deployed, not the user's home directory.
|
|
18
|
-
*/
|
|
19
|
-
export declare function compileAgentDefinitionActions(entry: AgentDefinitionEntry, sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string): Promise<AgentDefinitionsAdapterResult>;
|
|
20
|
-
export declare function compileAgentDefinitionReverts(entry: AgentDefinitionEntry, agentFilter: AgentId[] | null, home: string, repo?: string): FileWriteRevertAction[];
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { AGENT_REGISTRY_BY_ID } from "../../config/agents.js";
|
|
3
|
-
import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
|
|
4
|
-
import { validateAgentRuleMarkdownPath, validateSourceFile, validateSourcePath, } from "../validation.js";
|
|
5
|
-
/**
|
|
6
|
-
* Compiles deploy actions for agentDefinitions manifest entries.
|
|
7
|
-
*
|
|
8
|
-
* Agent definition files are Markdown files (typically with YAML frontmatter)
|
|
9
|
-
* that define custom agents or subagents for a given platform. Unlike
|
|
10
|
-
* agentRules (which target a single shared global file per agent), each
|
|
11
|
-
* definition entry produces a separate file named after the entry in the
|
|
12
|
-
* agent's dedicated agent-definitions directory (repo-local).
|
|
13
|
-
*
|
|
14
|
-
* Target paths use the `{repo}` placeholder so definitions land in the
|
|
15
|
-
* repository being deployed, not the user's home directory.
|
|
16
|
-
*/
|
|
17
|
-
export async function compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo) {
|
|
18
|
-
const actions = [];
|
|
19
|
-
const warnings = [];
|
|
20
|
-
const platform = getPlatformKey();
|
|
21
|
-
const targetAgents = entry.agents.filter((agentId) => detectedAgents.includes(agentId));
|
|
22
|
-
const supportedTargets = [];
|
|
23
|
-
if (targetAgents.length === 0) {
|
|
24
|
-
return { actions, warnings };
|
|
25
|
-
}
|
|
26
|
-
for (const agentId of targetAgents) {
|
|
27
|
-
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
28
|
-
const support = agent?.agentDefinitionsSupport;
|
|
29
|
-
if (!support || support.status === "unsupported") {
|
|
30
|
-
warnings.push({
|
|
31
|
-
kind: "confidence",
|
|
32
|
-
message: `agentDefinitions: agent "${agentId}" uses ${support?.schemaLabel ?? "an unsupported agent-definition schema"} and ${support?.status === "unsupported" ? support.reason : "does not expose a supported agent-definitions adapter"} — skipping "${entry.name}"`,
|
|
33
|
-
});
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
if (support.status === "planned") {
|
|
37
|
-
warnings.push({
|
|
38
|
-
kind: "confidence",
|
|
39
|
-
message: `agentDefinitions: agent "${agentId}" agent-definitions support is planned via ${support.plannedSurface} — skipping "${entry.name}" until that surface is implemented`,
|
|
40
|
-
});
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
supportedTargets.push({
|
|
44
|
-
agentId,
|
|
45
|
-
confidence: agent.provenance.agentDefinitions ?? "provisional",
|
|
46
|
-
target: resolvePlaceholders(support.path[platform], entry.name, home, repo),
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
if (supportedTargets.length === 0) {
|
|
50
|
-
return { actions, warnings };
|
|
51
|
-
}
|
|
52
|
-
// Validate the shared source file only when at least one target is active.
|
|
53
|
-
const source = path.resolve(sourceDir, entry.path);
|
|
54
|
-
await validateSourcePath(source, entry.path, resolvedSourceDir, realRoot);
|
|
55
|
-
await validateSourceFile(source, entry.path);
|
|
56
|
-
for (const target of supportedTargets) {
|
|
57
|
-
validateAgentRuleMarkdownPath(entry.path, target.agentId);
|
|
58
|
-
actions.push({
|
|
59
|
-
kind: "file-write",
|
|
60
|
-
skill: entry.name,
|
|
61
|
-
agent: target.agentId,
|
|
62
|
-
source,
|
|
63
|
-
target: target.target,
|
|
64
|
-
confidence: target.confidence,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
return { actions, warnings };
|
|
68
|
-
}
|
|
69
|
-
export function compileAgentDefinitionReverts(entry, agentFilter, home, repo) {
|
|
70
|
-
const actions = [];
|
|
71
|
-
const platform = getPlatformKey();
|
|
72
|
-
for (const agentId of entry.agents) {
|
|
73
|
-
if (agentFilter && !agentFilter.includes(agentId))
|
|
74
|
-
continue;
|
|
75
|
-
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
76
|
-
const support = agent?.agentDefinitionsSupport;
|
|
77
|
-
if (!support ||
|
|
78
|
-
support.status === "unsupported" ||
|
|
79
|
-
support.status === "planned")
|
|
80
|
-
continue;
|
|
81
|
-
const target = resolvePlaceholders(support.path[platform], entry.name, home, repo);
|
|
82
|
-
actions.push({
|
|
83
|
-
kind: "file-write",
|
|
84
|
-
skill: entry.name,
|
|
85
|
-
agent: agentId,
|
|
86
|
-
target,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
return actions;
|
|
90
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { AGENT_REGISTRY_BY_ID } from "../../config/agents.js";
|
|
3
|
-
import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
|
|
4
|
-
import { validateAgentRuleMarkdownPath, validateSourceFile, validateSourcePath, } from "../validation.js";
|
|
5
|
-
function resolveRulesSupport(agentId, scope) {
|
|
6
|
-
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
7
|
-
if (scope === "repo") {
|
|
8
|
-
return agent?.agentRulesRepoSupport ?? agent?.agentRulesSupport;
|
|
9
|
-
}
|
|
10
|
-
return agent?.agentRulesSupport;
|
|
11
|
-
}
|
|
12
|
-
function resolveAgentTarget(agentId, entry, home, repo, platform) {
|
|
13
|
-
const support = resolveRulesSupport(agentId, entry.scope);
|
|
14
|
-
if (!support || support.status === "unsupported") {
|
|
15
|
-
return {
|
|
16
|
-
kind: "confidence",
|
|
17
|
-
message: `agentRules: agent "${agentId}" uses ${support?.schemaLabel ?? "an unsupported instruction schema"} and ${support?.status === "unsupported" ? support.reason : "does not expose a supported rules adapter"} — skipping "${entry.name}"`,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
if (support.status === "planned") {
|
|
21
|
-
return {
|
|
22
|
-
kind: "confidence",
|
|
23
|
-
message: `agentRules: agent "${agentId}" rules support is planned via ${support.plannedSurface} — skipping "${entry.name}" until that surface is implemented`,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
if (entry.scope === "repo" && !repo) {
|
|
27
|
-
return {
|
|
28
|
-
kind: "confidence",
|
|
29
|
-
message: `agentRules: scope "repo" requires a repository path but none was resolved — skipping "${entry.name}" for agent "${agentId}"`,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
33
|
-
return {
|
|
34
|
-
agentId,
|
|
35
|
-
confidence: agent?.provenance.agentRules ?? "provisional",
|
|
36
|
-
target: resolvePlaceholders(support.path[platform], entry.name, home, repo),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
export async function compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo) {
|
|
40
|
-
const actions = [];
|
|
41
|
-
const warnings = [];
|
|
42
|
-
const platform = getPlatformKey();
|
|
43
|
-
const targetAgents = entry.agents.filter((agentId) => detectedAgents.includes(agentId));
|
|
44
|
-
const supportedTargets = [];
|
|
45
|
-
if (targetAgents.length === 0) {
|
|
46
|
-
return { actions, warnings };
|
|
47
|
-
}
|
|
48
|
-
for (const agentId of targetAgents) {
|
|
49
|
-
const result = resolveAgentTarget(agentId, entry, home, repo, platform);
|
|
50
|
-
if ("kind" in result) {
|
|
51
|
-
warnings.push(result);
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
supportedTargets.push(result);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (supportedTargets.length === 0) {
|
|
58
|
-
return { actions, warnings };
|
|
59
|
-
}
|
|
60
|
-
// Validate the shared source file only when at least one target uses the
|
|
61
|
-
// current rules adapter surface.
|
|
62
|
-
const source = path.resolve(sourceDir, entry.path);
|
|
63
|
-
await validateSourcePath(source, entry.path, resolvedSourceDir, realRoot);
|
|
64
|
-
await validateSourceFile(source, entry.path);
|
|
65
|
-
for (const target of supportedTargets) {
|
|
66
|
-
validateAgentRuleMarkdownPath(entry.path, target.agentId);
|
|
67
|
-
actions.push({
|
|
68
|
-
kind: "file-write",
|
|
69
|
-
skill: entry.name,
|
|
70
|
-
agent: target.agentId,
|
|
71
|
-
source,
|
|
72
|
-
target: target.target,
|
|
73
|
-
confidence: target.confidence,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
return { actions, warnings };
|
|
77
|
-
}
|
|
78
|
-
export function compileAgentRuleReverts(entry, agentFilter, home, repo) {
|
|
79
|
-
const actions = [];
|
|
80
|
-
const platform = getPlatformKey();
|
|
81
|
-
for (const agentId of entry.agents) {
|
|
82
|
-
if (agentFilter && !agentFilter.includes(agentId))
|
|
83
|
-
continue;
|
|
84
|
-
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
85
|
-
const support = entry.scope === "repo"
|
|
86
|
-
? (agent?.agentRulesRepoSupport ?? agent?.agentRulesSupport)
|
|
87
|
-
: agent?.agentRulesSupport;
|
|
88
|
-
if (!support ||
|
|
89
|
-
support.status === "unsupported" ||
|
|
90
|
-
support.status === "planned")
|
|
91
|
-
continue;
|
|
92
|
-
if (entry.scope === "repo" && !repo)
|
|
93
|
-
continue;
|
|
94
|
-
const target = resolvePlaceholders(support.path[platform], entry.name, home, repo);
|
|
95
|
-
actions.push({
|
|
96
|
-
kind: "file-write",
|
|
97
|
-
skill: entry.name,
|
|
98
|
-
agent: agentId,
|
|
99
|
-
target,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
return actions;
|
|
103
|
-
}
|
package/dist/core/preflight.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { stat } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
4
|
-
const BUDGET_WARN_BYTES = 50 * 1024; // 50 KB
|
|
5
|
-
function detectInstructionPrecedence(detectedAgents, manifest) {
|
|
6
|
-
const warnings = [];
|
|
7
|
-
for (const agentId of detectedAgents) {
|
|
8
|
-
const rulesForAgent = (manifest.agentRules ?? []).filter((e) => e.agents.includes(agentId));
|
|
9
|
-
const globalEntries = rulesForAgent.filter((e) => (e.scope ?? "global") === "global");
|
|
10
|
-
const repoEntries = rulesForAgent.filter((e) => e.scope === "repo");
|
|
11
|
-
if (globalEntries.length === 0 || repoEntries.length === 0)
|
|
12
|
-
continue;
|
|
13
|
-
const globalPaths = new Set(globalEntries.map((e) => e.path));
|
|
14
|
-
for (const entry of repoEntries) {
|
|
15
|
-
if (globalPaths.has(entry.path)) {
|
|
16
|
-
warnings.push({
|
|
17
|
-
kind: "precedence",
|
|
18
|
-
message: `Agent "${agentId}" has agentRules entry "${entry.name}" deployed to both global and repo scope from the same source path "${entry.path}". The file will be written to two distinct targets — verify this is intentional and not a copy-paste mistake.`,
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
const nonOverlapRepo = repoEntries.filter((e) => !globalPaths.has(e.path));
|
|
23
|
-
if (nonOverlapRepo.length > 0) {
|
|
24
|
-
const globalNames = globalEntries.map((e) => `"${e.name}"`).join(", ");
|
|
25
|
-
const repoNames = nonOverlapRepo.map((e) => `"${e.name}"`).join(", ");
|
|
26
|
-
warnings.push({
|
|
27
|
-
kind: "precedence",
|
|
28
|
-
message: `Agent "${agentId}" will have both global and repo instruction files active simultaneously: global [${globalNames}] and repo [${repoNames}]. Both will be loaded by the agent — ensure the content is intended to stack and does not conflict.`,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return warnings;
|
|
33
|
-
}
|
|
34
|
-
async function detectInstructionBudgetRisk(detectedAgents, manifest, sourceDir) {
|
|
35
|
-
const warnings = [];
|
|
36
|
-
const checkedPaths = new Set();
|
|
37
|
-
async function checkEntry(entryPath, label, agentIds) {
|
|
38
|
-
if (!agentIds.some((id) => detectedAgents.includes(id)))
|
|
39
|
-
return;
|
|
40
|
-
const sourcePath = path.resolve(sourceDir, entryPath);
|
|
41
|
-
if (checkedPaths.has(sourcePath))
|
|
42
|
-
return;
|
|
43
|
-
checkedPaths.add(sourcePath);
|
|
44
|
-
try {
|
|
45
|
-
const fileStat = await stat(sourcePath);
|
|
46
|
-
if (fileStat.size > BUDGET_WARN_BYTES) {
|
|
47
|
-
const sizeKb = (fileStat.size / 1024).toFixed(1);
|
|
48
|
-
warnings.push({
|
|
49
|
-
kind: "budget",
|
|
50
|
-
message: `${label} source "${entryPath}" is ${sizeKb} KB — large instruction files risk crowding out code context in the agent's context window. Consider splitting into smaller focused files.`,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
catch (err) {
|
|
55
|
-
const code = err.code;
|
|
56
|
-
if (code !== "ENOENT" && code !== "EACCES" && code !== "EPERM")
|
|
57
|
-
throw err;
|
|
58
|
-
// Missing/unreadable files are silently skipped; compileAgentRuleActions
|
|
59
|
-
// will produce the proper UserError during action compilation.
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
for (const entry of manifest.agentRules ?? []) {
|
|
63
|
-
await checkEntry(entry.path, "agentRules", entry.agents);
|
|
64
|
-
}
|
|
65
|
-
for (const entry of manifest.agentDefinitions ?? []) {
|
|
66
|
-
await checkEntry(entry.path, "agentDefinitions", entry.agents);
|
|
67
|
-
}
|
|
68
|
-
return warnings;
|
|
69
|
-
}
|
|
70
|
-
export async function runPreflight(options, manifest, _home, detectedAgents) {
|
|
71
|
-
const warnings = [];
|
|
72
|
-
for (const agentId of detectedAgents) {
|
|
73
|
-
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
74
|
-
if (!agent)
|
|
75
|
-
continue;
|
|
76
|
-
if (agent.provenance.skills === "implementation-only") {
|
|
77
|
-
warnings.push({
|
|
78
|
-
kind: "config-authority",
|
|
79
|
-
message: `Agent "${agentId}" skill support is implementation-only: paths are derived from source inspection, not published documentation.`,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
else if (agent.provenance.skills === "provisional") {
|
|
83
|
-
warnings.push({
|
|
84
|
-
kind: "config-authority",
|
|
85
|
-
message: `Agent "${agentId}" skill support is provisional: behavior has not been independently verified.`,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
if (agent.policyNote) {
|
|
89
|
-
warnings.push({
|
|
90
|
-
kind: "policy",
|
|
91
|
-
message: `Agent "${agentId}": ${agent.policyNote}`,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
warnings.push(...detectInstructionPrecedence(detectedAgents, manifest));
|
|
96
|
-
warnings.push(...(await detectInstructionBudgetRisk(detectedAgents, manifest, options.directory)));
|
|
97
|
-
return warnings;
|
|
98
|
-
}
|
package/dist/logger.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import process from "node:process";
|
|
2
|
-
const C = {
|
|
3
|
-
red: "\x1b[31m",
|
|
4
|
-
green: "\x1b[32m",
|
|
5
|
-
yellow: "\x1b[33m",
|
|
6
|
-
cyan: "\x1b[36m",
|
|
7
|
-
reset: "\x1b[0m",
|
|
8
|
-
};
|
|
9
|
-
export function createLogger() {
|
|
10
|
-
let silent = false;
|
|
11
|
-
const tty = Boolean(process.stdout.isTTY);
|
|
12
|
-
const errTTY = Boolean(process.stderr.isTTY);
|
|
13
|
-
const c = (col, text, onTTY) => onTTY ? `${C[col]}${text}${C.reset}` : text;
|
|
14
|
-
return {
|
|
15
|
-
fail(label, msg) {
|
|
16
|
-
if (!silent)
|
|
17
|
-
process.stderr.write(` ${c("red", "✗", errTTY)} ${label}: ${msg}\n`);
|
|
18
|
-
},
|
|
19
|
-
ok(label) {
|
|
20
|
-
if (!silent)
|
|
21
|
-
process.stdout.write(` ${c("green", "✓", tty)} ${label}\n`);
|
|
22
|
-
},
|
|
23
|
-
skip(label, note) {
|
|
24
|
-
if (!silent)
|
|
25
|
-
process.stdout.write(` ${c("yellow", "-", tty)} ${label} ${note}\n`);
|
|
26
|
-
},
|
|
27
|
-
plan(label) {
|
|
28
|
-
if (!silent)
|
|
29
|
-
process.stdout.write(` ${c("cyan", "○", tty)} ${label}\n`);
|
|
30
|
-
},
|
|
31
|
-
warn(label, msg) {
|
|
32
|
-
if (!silent)
|
|
33
|
-
process.stdout.write(` ${c("yellow", "!", tty)} ${label}: ${msg}\n`);
|
|
34
|
-
},
|
|
35
|
-
info(msg) {
|
|
36
|
-
if (!silent)
|
|
37
|
-
process.stdout.write(`${msg}\n`);
|
|
38
|
-
},
|
|
39
|
-
error(msg) {
|
|
40
|
-
if (!silent)
|
|
41
|
-
process.stderr.write(`${msg}\n`);
|
|
42
|
-
},
|
|
43
|
-
errorRaw(value) {
|
|
44
|
-
if (!silent)
|
|
45
|
-
console.error(value);
|
|
46
|
-
},
|
|
47
|
-
detail(msg) {
|
|
48
|
-
if (!silent)
|
|
49
|
-
process.stdout.write(` ${msg}\n`);
|
|
50
|
-
},
|
|
51
|
-
silence() {
|
|
52
|
-
silent = true;
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
export const logger = createLogger();
|
|
57
|
-
export function dryRunPrefix(dryRun) {
|
|
58
|
-
if (!dryRun)
|
|
59
|
-
return "";
|
|
60
|
-
return process.stdout.isTTY ? `\x1b[36m[dry-run]\x1b[0m ` : `[dry-run] `;
|
|
61
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|