Skip to content

Conversation

@dyoshikawa
Copy link
Owner

Summary

  • add Kilo Code rule generator covering project and global .kilocode/rules layouts
  • document Kilo Code rule locations and include new tool target metadata and tests

Testing

  • pnpm cicheck

Resolves #185


Codex Task

Copilot AI review requested due to automatic review settings December 29, 2025 14:25
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +57 to +61
this.buildToolRuleParamsDefault({
baseDir,
rulesyncRule,
validate,
nonRootPath: this.getSettablePaths().nonRoot,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Global Kilo export ignores non-root rules

Kilo is now marked as supportsGlobal, but fromRulesyncRule never sets a Kilo-specific root path—root rules fall back to ToolRule’s default ./AGENTS.md and global mode in RulesProcessor only forwards root rules (see loadRulesyncFiles at lines 612‑618). Running the processor with toolTarget: "kilo" and global: true (e.g., rulesync generate --targets kilo --global) will emit a single AGENTS.md in the base directory and drop every non-root rulesync rule, so no files are written under ~/.kilocode/rules even though Kilo expects its global rules there. This leaves global Kilo generation non-functional.

Useful? React with 👍 / 👎.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for Kilo Code as a new tool target in Rulesync, enabling generation of Kilo Code rules from unified rulesync rules.

Key Changes:

  • Adds KiloRule class implementing Kilo Code's .kilocode/rules/*.md directory-based rules format
  • Configures Kilo Code with global mode support (supportsGlobal: true) and auto rule discovery mode
  • Updates all necessary configuration files, tests, and documentation

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/types/tool-targets.ts Adds "kilo" to the list of supported tool targets in alphabetical order
src/types/tool-targets.test.ts Updates test expectations to include "kilo" target
src/features/rules/rules-processor.ts Integrates KiloRule with processor, imports class, adds to factory map with global support enabled
src/features/rules/rules-processor.test.ts Updates global targets tests to include "kilo" among the 5 global-supporting tools
src/features/rules/kilo-rule.ts New file implementing KiloRule class for .kilocode/rules/*.md generation, but missing global parameter handling
src/features/rules/kilo-rule.test.ts New file with test coverage for basic KiloRule operations, but missing global mode tests
cspell.json Adds Kilo-related words to the spell-check dictionary
config-schema.json Adds "kilo" to the JSON schema enum for valid tool targets
README.md Documents Kilo Code support in the features table and adds section explaining .kilocode/rules/*.md structure

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 25 to 31
static getSettablePaths(): KiloRuleSettablePaths {
return {
nonRoot: {
relativeDirPath: join(".kilocode", "rules"),
},
};
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getSettablePaths method should accept a global parameter since this rule is configured with supportsGlobal: true in rules-processor.ts. When global mode is enabled, rules-processor calls getSettablePaths with a global parameter. Other rules that support global mode (like GeminiCliRule and CodexcliRule) implement getSettablePaths with signature: getSettablePaths({ global }: { global?: boolean } = {}). The method should return the same path structure regardless of global mode, as the baseDir will be set to the home directory by ConfigResolver when global is true.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +49
static async fromFile({
baseDir = process.cwd(),
relativeFilePath,
validate = true,
}: ToolRuleFromFileParams): Promise<KiloRule> {
const fileContent = await readFileContent(
join(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath),
);

return new KiloRule({
baseDir,
relativeDirPath: this.getSettablePaths().nonRoot.relativeDirPath,
relativeFilePath: relativeFilePath,
fileContent,
validate,
});
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fromFile method should accept and pass through a global parameter. Rules-processor passes global: this.global when calling fromFile. Other global-supporting rules like GeminiCliRule and CodexcliRule include global = false in their ToolRuleFromFileParams. This parameter should be passed to the KiloRule constructor.

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +64
static fromRulesyncRule({
baseDir = process.cwd(),
rulesyncRule,
validate = true,
}: ToolRuleFromRulesyncRuleParams): KiloRule {
return new KiloRule(
this.buildToolRuleParamsDefault({
baseDir,
rulesyncRule,
validate,
nonRootPath: this.getSettablePaths().nonRoot,
}),
);
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fromRulesyncRule method should accept a global parameter. Rules-processor passes global: this.global when calling fromRulesyncRule. Other global-supporting rules like GeminiCliRule and CodexcliRule include global = false in their ToolRuleFromRulesyncRuleParams. This parameter should be passed through to buildToolRuleParamsDefault and the KiloRule constructor.

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +86
static forDeletion({
baseDir = process.cwd(),
relativeDirPath,
relativeFilePath,
}: ToolRuleForDeletionParams): KiloRule {
return new KiloRule({
baseDir,
relativeDirPath,
relativeFilePath,
fileContent: "",
validate: false,
});
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The forDeletion method should accept a global parameter. Rules-processor passes global: this.global when calling forDeletion. Other global-supporting rules like GeminiCliRule and CodexcliRule include global = false in their ToolRuleForDeletionParams. This parameter should be passed to the KiloRule constructor.

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 110
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
RULESYNC_RELATIVE_DIR_PATH,
RULESYNC_RULES_RELATIVE_DIR_PATH,
} from "../../constants/rulesync-paths.js";
import { setupTestDirectory } from "../../test-utils/test-directories.js";
import { ensureDir, writeFileContent } from "../../utils/file.js";
import { KiloRule } from "./kilo-rule.js";
import { RulesyncRule } from "./rulesync-rule.js";

describe("KiloRule", () => {
let testDir: string;
let cleanup: () => Promise<void>;

beforeEach(async () => {
({ testDir, cleanup } = await setupTestDirectory());
vi.spyOn(process, "cwd").mockReturnValue(testDir);
});

afterEach(async () => {
await cleanup();
vi.restoreAllMocks();
});

describe("fromFile", () => {
it("should read rule content from the .kilocode/rules directory", async () => {
const rulesDir = join(testDir, ".kilocode/rules");
await ensureDir(rulesDir);
const filePath = join(rulesDir, "test-rule.md");
await writeFileContent(filePath, "# Kilo Rule\n\nBody");

const kiloRule = await KiloRule.fromFile({
baseDir: testDir,
relativeFilePath: "test-rule.md",
});

expect(kiloRule.getRelativeDirPath()).toBe(".kilocode/rules");
expect(kiloRule.getRelativeFilePath()).toBe("test-rule.md");
expect(kiloRule.getFileContent()).toBe("# Kilo Rule\n\nBody");
expect(kiloRule.getFilePath()).toBe(filePath);
});
});

describe("fromRulesyncRule", () => {
it("should build rule parameters from a Rulesync rule", () => {
const rulesyncRule = new RulesyncRule({
baseDir: testDir,
relativeDirPath: RULESYNC_RELATIVE_DIR_PATH,
relativeFilePath: "kilo.md",
frontmatter: { targets: ["kilo"] },
body: "# From Rulesync",
});

const kiloRule = KiloRule.fromRulesyncRule({
baseDir: testDir,
rulesyncRule,
});

expect(kiloRule.getRelativeDirPath()).toBe(".kilocode/rules");
expect(kiloRule.getRelativeFilePath()).toBe("kilo.md");
expect(kiloRule.getFileContent()).toBe("# From Rulesync");
});
});

describe("toRulesyncRule", () => {
it("should convert back to a RulesyncRule", () => {
const kiloRule = new KiloRule({
baseDir: testDir,
relativeDirPath: ".kilocode/rules",
relativeFilePath: "team.md",
fileContent: "# Team Rules",
});

const rulesyncRule = kiloRule.toRulesyncRule();

expect(rulesyncRule.getRelativeDirPath()).toBe(RULESYNC_RULES_RELATIVE_DIR_PATH);
expect(rulesyncRule.getRelativeFilePath()).toBe("team.md");
expect(rulesyncRule.getBody()).toBe("# Team Rules");
expect(rulesyncRule.getFrontmatter().targets).toEqual(["*"]);
});
});

describe("forDeletion", () => {
it("should create a non-validated rule for cleanup", () => {
const rule = KiloRule.forDeletion({
baseDir: testDir,
relativeDirPath: ".kilocode/rules",
relativeFilePath: "obsolete.md",
});

expect(rule.isDeletable()).toBe(true);
expect(rule.getFilePath()).toBe(join(testDir, ".kilocode/rules/obsolete.md"));
});
});

describe("isTargetedByRulesyncRule", () => {
it("should detect rulesync frontmatter targeting Kilo Code", () => {
const rulesyncRule = new RulesyncRule({
baseDir: testDir,
relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,
relativeFilePath: "kilo.md",
frontmatter: { targets: ["kilo"] },
body: "# Targeted",
});

expect(KiloRule.isTargetedByRulesyncRule(rulesyncRule)).toBe(true);
});
});
});
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test suite is missing coverage for global mode functionality. Since KiloRule is configured with supportsGlobal: true in rules-processor.ts, tests should verify the behavior when global parameter is true. Compare with codexcli-rule.test.ts which includes tests for getSettablePaths with global flag, fromFile with global flag, and fromRulesyncRule with global flag. These tests ensure that global mode paths are correctly resolved and that the rule can be properly instantiated in global mode.

Copilot uses AI. Check for mistakes.
dyoshikawa and others added 8 commits December 30, 2025 12:04
- Fix README.md table formatting by removing empty line between rows
- Add global option parameter to getSettablePaths for consistency with base class
- Add test case for getSettablePaths global mode behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Remove unused RULESYNC_RELATIVE_DIR_PATH import from kilo-rule.test.ts
- Remove unused KiloRuleParams type export from kilo-rule.ts
- Add comprehensive tests following cline-rule.test.ts pattern:
  - constructor tests with various parameters
  - validate() method tests including edge cases
  - fromFile tests with validation and nested directories
  - fromRulesyncRule tests with custom baseDir
  - isTargetedByRulesyncRule edge cases
  - ToolRule base class integration tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
…-feature-based-on-issue-185

# Conflicts:
#	.devcontainer/Dockerfile
#	README.md
Add "kilorules" word to the cspell dictionary to fix spell check
failures for the Kilo Code legacy file name format in tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@dyoshikawa dyoshikawa merged commit c3998f1 into main Jan 1, 2026
9 checks passed
@dyoshikawa dyoshikawa deleted the codex/implement-rules-feature-based-on-issue-185 branch January 1, 2026 07:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for Kilo

1 participant