@chamba/core 0.3.2 → 0.5.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/dist/index.d.ts +41 -1
- package/dist/index.js +164 -28
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -420,6 +420,40 @@ declare class VaultWriter {
|
|
|
420
420
|
write(input: WriteNoteInput): Promise<WriteNoteResult>;
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
+
interface RuleConvention {
|
|
424
|
+
editor: string;
|
|
425
|
+
/** Path relative to a repo root. */
|
|
426
|
+
path: string;
|
|
427
|
+
kind: 'file' | 'dir';
|
|
428
|
+
}
|
|
429
|
+
declare const RULE_SOURCES: readonly RuleConvention[];
|
|
430
|
+
/** A concrete rule file found in a repo. */
|
|
431
|
+
interface RuleSource {
|
|
432
|
+
/** Repo path relative to the workspace root (`.` for the root itself). */
|
|
433
|
+
repo: string;
|
|
434
|
+
editor: string;
|
|
435
|
+
/** Rule file path relative to the workspace root. */
|
|
436
|
+
path: string;
|
|
437
|
+
}
|
|
438
|
+
interface RuleExcerpt {
|
|
439
|
+
source: RuleSource;
|
|
440
|
+
excerpt: string;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Discover coding-rule files across the workspace root and each repo, for every
|
|
444
|
+
* editor convention in the catalog. Non-exclusive: a repo can match several.
|
|
445
|
+
* `repoDirs` are paths relative to the workspace root (`.` for the root).
|
|
446
|
+
*/
|
|
447
|
+
declare function detectRuleSources(fs: FilesystemPort, workspaceRoot: string, repoDirs: string[]): Promise<RuleSource[]>;
|
|
448
|
+
/**
|
|
449
|
+
* Read each rule file fresh and return a clamped excerpt, bounded by a total
|
|
450
|
+
* budget so rules never crowd out the rest of the context.
|
|
451
|
+
*/
|
|
452
|
+
declare function readRuleExcerpts(fs: FilesystemPort, workspaceRoot: string, sources: RuleSource[], opts?: {
|
|
453
|
+
maxCharsPerRule?: number;
|
|
454
|
+
totalBudget?: number;
|
|
455
|
+
}): Promise<RuleExcerpt[]>;
|
|
456
|
+
|
|
423
457
|
/** Relative path (from workspace root) of the chamba workspace file. */
|
|
424
458
|
declare const WORKSPACE_DIR = ".chamba";
|
|
425
459
|
declare const WORKSPACE_FILE = "workspace.md";
|
|
@@ -438,6 +472,8 @@ interface Workspace {
|
|
|
438
472
|
framework?: string;
|
|
439
473
|
conventions: string[];
|
|
440
474
|
projects: ProjectRef[];
|
|
475
|
+
/** Coding-rule files found across repos (Cursor, Claude, Trae, …). */
|
|
476
|
+
ruleSources: RuleSource[];
|
|
441
477
|
/** Top-level directory names (without trailing slash), sorted. */
|
|
442
478
|
folderMap: string[];
|
|
443
479
|
}
|
|
@@ -588,6 +624,8 @@ interface ContextBuildInput {
|
|
|
588
624
|
task: string;
|
|
589
625
|
/** When set, search this Obsidian vault for notes relevant to the task. */
|
|
590
626
|
vaultPath?: string;
|
|
627
|
+
/** Include a section with each repo's coding rules (default true). */
|
|
628
|
+
includeRules?: boolean;
|
|
591
629
|
/** Soft cap on the produced context, in estimated tokens (~4 chars/token). */
|
|
592
630
|
maxTokens?: number;
|
|
593
631
|
}
|
|
@@ -604,6 +642,8 @@ declare class ContextBuilder {
|
|
|
604
642
|
private readonly fs;
|
|
605
643
|
constructor(fs: FilesystemPort);
|
|
606
644
|
build(input: ContextBuildInput): Promise<BuiltContext>;
|
|
645
|
+
/** Each repo's coding rules (read fresh, clamped), non-exclusive across editors. */
|
|
646
|
+
private codingRulesSection;
|
|
607
647
|
private workspaceSection;
|
|
608
648
|
private notesSection;
|
|
609
649
|
private searchNotes;
|
|
@@ -847,4 +887,4 @@ declare class MultiRepoWorktreeManager {
|
|
|
847
887
|
private git;
|
|
848
888
|
}
|
|
849
889
|
|
|
850
|
-
export { AGENT_ROLES, type AgentConfig, type AgentRole, type BranchNameInput, type BuiltContext, type ChambaConfig, type CleanupMultiResult, type CleanupResult, type ClockPort, ConfigError, type ConfigFile, type ConfigSource, type ConfigSourceKind, type ContextBuildInput, ContextBuilder, type CreateMultiInput, type CreateWorktreeInput, DEFAULT_CONFIG, DEFAULT_WORKTREE_CONFIG, type DetectOptions, type DirEntry, EFFORT_LEVELS, type Effort, FakeProcess, FilesystemMemoryStore, type FilesystemPort, type GeneratePlanInput, GitDetector, type Issue, type IssueSeverity, type ListedWorktree, type LoadConfigOptions, type LoadConfigResult, MEMORY_DIR, MODEL_CATALOG, type Memory, MemoryFilesystem, type MemoryStore, type ModelInfo, type ModelProvider, MultiRepoWorktreeManager, type MultiRepoWorktreeResult, type NoteFields, ObsidianDetector, type ParseResult, type PartialWorktreeConfig, type PlanReview, type PlanWorktreesInput, type ProcessExecOptions, type ProcessHandler, type ProcessPort, type ProcessResult, type ProjectRef, REASONING_PRIORITIES, ROLE_DESCRIPTIONS, type ReasoningPriority, type RecordedCall, type RelevantNote, type RememberInput, type ResolvedConfig, type ReviewInput, Reviewer, type SubtaskSpec, VAULT_NOTES_DIR, type ValidatePlanInput, type ValidationResult, type VaultDetection, VaultWriter, WORKSPACE_DIR, WORKSPACE_FILE, WORKSPACE_RELATIVE_PATH, type WorkerKind, type Workspace, WorkspaceScanner, type WorktreeConfig, WorktreeError, type WorktreeHandle, type WorktreeLayout, WorktreeManager, type WorktreePlanItem, type WorktreeStatus, type WriteNoteInput, type WriteNoteResult, basename, buildBranchName, buildHint, buildTicketBranch, configFileSchema, copyEnvFiles, detectGitRepos, diffLines, dirname, editorWorkspaceContent, editorWorkspaceDir, extname, generatePlanTemplate, getModel, joinPath, listVaultNotes, loadConfig, modelsByProvider, normalizeVaultPath, parseChambaConfig, planWorktrees, renderNote, renderWorkspaceMarkdown, resolveEffort, resolveRole, resolveWorktreeConfig, safeTicket, slugify, slugifyForGit, suggestFilesLikelyTouched, suggestSubtasks, textsEqual, validatePlan, worktreeConfigSchema, worktreePathFor, worktreeRelativePath, writeEditorWorkspace };
|
|
890
|
+
export { AGENT_ROLES, type AgentConfig, type AgentRole, type BranchNameInput, type BuiltContext, type ChambaConfig, type CleanupMultiResult, type CleanupResult, type ClockPort, ConfigError, type ConfigFile, type ConfigSource, type ConfigSourceKind, type ContextBuildInput, ContextBuilder, type CreateMultiInput, type CreateWorktreeInput, DEFAULT_CONFIG, DEFAULT_WORKTREE_CONFIG, type DetectOptions, type DirEntry, EFFORT_LEVELS, type Effort, FakeProcess, FilesystemMemoryStore, type FilesystemPort, type GeneratePlanInput, GitDetector, type Issue, type IssueSeverity, type ListedWorktree, type LoadConfigOptions, type LoadConfigResult, MEMORY_DIR, MODEL_CATALOG, type Memory, MemoryFilesystem, type MemoryStore, type ModelInfo, type ModelProvider, MultiRepoWorktreeManager, type MultiRepoWorktreeResult, type NoteFields, ObsidianDetector, type ParseResult, type PartialWorktreeConfig, type PlanReview, type PlanWorktreesInput, type ProcessExecOptions, type ProcessHandler, type ProcessPort, type ProcessResult, type ProjectRef, REASONING_PRIORITIES, ROLE_DESCRIPTIONS, RULE_SOURCES, type ReasoningPriority, type RecordedCall, type RelevantNote, type RememberInput, type ResolvedConfig, type ReviewInput, Reviewer, type RuleConvention, type RuleExcerpt, type RuleSource, type SubtaskSpec, VAULT_NOTES_DIR, type ValidatePlanInput, type ValidationResult, type VaultDetection, VaultWriter, WORKSPACE_DIR, WORKSPACE_FILE, WORKSPACE_RELATIVE_PATH, type WorkerKind, type Workspace, WorkspaceScanner, type WorktreeConfig, WorktreeError, type WorktreeHandle, type WorktreeLayout, WorktreeManager, type WorktreePlanItem, type WorktreeStatus, type WriteNoteInput, type WriteNoteResult, basename, buildBranchName, buildHint, buildTicketBranch, configFileSchema, copyEnvFiles, detectGitRepos, detectRuleSources, diffLines, dirname, editorWorkspaceContent, editorWorkspaceDir, extname, generatePlanTemplate, getModel, joinPath, listVaultNotes, loadConfig, modelsByProvider, normalizeVaultPath, parseChambaConfig, planWorktrees, readRuleExcerpts, renderNote, renderWorkspaceMarkdown, resolveEffort, resolveRole, resolveWorktreeConfig, safeTicket, slugify, slugifyForGit, suggestFilesLikelyTouched, suggestSubtasks, textsEqual, validatePlan, worktreeConfigSchema, worktreePathFor, worktreeRelativePath, writeEditorWorkspace };
|
package/dist/index.js
CHANGED
|
@@ -415,6 +415,19 @@ function renderWorkspaceMarkdown(ws) {
|
|
|
415
415
|
lines.push("_None detected._");
|
|
416
416
|
}
|
|
417
417
|
lines.push("");
|
|
418
|
+
lines.push("## Coding rules");
|
|
419
|
+
lines.push("");
|
|
420
|
+
lines.push("> Rule files found per repo (read non-exclusively across editors). chamba");
|
|
421
|
+
lines.push("> loads their content into context at task time.");
|
|
422
|
+
lines.push("");
|
|
423
|
+
if (ws.ruleSources.length > 0) {
|
|
424
|
+
for (const r of ws.ruleSources) {
|
|
425
|
+
lines.push(`- \`${r.path}\` \u2014 ${r.editor} (${r.repo})`);
|
|
426
|
+
}
|
|
427
|
+
} else {
|
|
428
|
+
lines.push("_None detected._");
|
|
429
|
+
}
|
|
430
|
+
lines.push("");
|
|
418
431
|
lines.push("## Active projects");
|
|
419
432
|
lines.push("");
|
|
420
433
|
if (ws.projects.length > 0) {
|
|
@@ -619,6 +632,8 @@ var SENSITIVE_PATH_RE = /(^|\/)(auth|payments|billing|migrations|secrets?|creden
|
|
|
619
632
|
var WORKER_RE = /\b(implementer|tester|reviewer|worker)\b/i;
|
|
620
633
|
var TESTS_RE = /\b(tests?|vitest|jest|spec|unit test|integration test)\b/i;
|
|
621
634
|
var PLACEHOLDER_RE = /\b(todo|tbd|fixme|placeholder)\b/i;
|
|
635
|
+
var DELETION_RE = /\b(delete[ds]?|deleting|remove[ds]?|removing|removal|drop(?:ping|s|ped)?|eliminat\w*|deprecat\w*|elimina\w*|borra\w*|quita\w*)\b/i;
|
|
636
|
+
var ORPHAN_CHECK_RE = /\b(orphan\w*|dead[-\s]?code|unused\s+(?:export|import|symbol|function|code|reference)|referential|callers?|knip|ts-prune|depcheck|type-?check|typecheck|tsc|build\s+(?:passes|green|clean))\b/i;
|
|
622
637
|
var DEFAULT_MODULES = /* @__PURE__ */ new Set([
|
|
623
638
|
"packages",
|
|
624
639
|
"examples",
|
|
@@ -703,6 +718,16 @@ function validatePlan(input) {
|
|
|
703
718
|
suggestions.push('Add a concrete entry under "## Risks" describing the risk and mitigation.');
|
|
704
719
|
}
|
|
705
720
|
}
|
|
721
|
+
if (DELETION_RE.test(plan) && !ORPHAN_CHECK_RE.test(plan)) {
|
|
722
|
+
issues.push({
|
|
723
|
+
code: "deletion-without-orphan-check",
|
|
724
|
+
severity: "warning",
|
|
725
|
+
message: "The plan removes code but never mentions verifying referential closure: orphaned callers or now-unused exports left behind. Token grep alone misses these."
|
|
726
|
+
});
|
|
727
|
+
suggestions.push(
|
|
728
|
+
"After removing code, verify nothing was orphaned: run the build/typecheck and a dead-code check (e.g. knip / ts-prune), not just a grep."
|
|
729
|
+
);
|
|
730
|
+
}
|
|
706
731
|
return { issues, suggestions, riskFlags };
|
|
707
732
|
}
|
|
708
733
|
function sections(plan) {
|
|
@@ -904,12 +929,12 @@ var MemoryFilesystem = class {
|
|
|
904
929
|
const prefix = norm === "/" ? "/" : `${norm}/`;
|
|
905
930
|
const seen = /* @__PURE__ */ new Set();
|
|
906
931
|
const entries = [];
|
|
907
|
-
const pushChild = (full,
|
|
932
|
+
const pushChild = (full, isDir2) => {
|
|
908
933
|
if (!full.startsWith(prefix) || full === norm) return;
|
|
909
934
|
const rest = full.slice(prefix.length);
|
|
910
935
|
const slash = rest.indexOf("/");
|
|
911
936
|
const name = slash === -1 ? rest : rest.slice(0, slash);
|
|
912
|
-
const childIsDir = slash !== -1 ||
|
|
937
|
+
const childIsDir = slash !== -1 || isDir2;
|
|
913
938
|
if (name.length === 0 || seen.has(name)) return;
|
|
914
939
|
seen.add(name);
|
|
915
940
|
entries.push({ name, isDirectory: childIsDir, isFile: !childIsDir });
|
|
@@ -920,6 +945,90 @@ var MemoryFilesystem = class {
|
|
|
920
945
|
}
|
|
921
946
|
};
|
|
922
947
|
|
|
948
|
+
// src/workspace/rules.ts
|
|
949
|
+
var RULE_SOURCES = [
|
|
950
|
+
{ editor: "Cursor", path: ".cursor/rules", kind: "dir" },
|
|
951
|
+
{ editor: "Cursor", path: ".cursorrules", kind: "file" },
|
|
952
|
+
{ editor: "Claude Code", path: "CLAUDE.md", kind: "file" },
|
|
953
|
+
{ editor: "Claude Code", path: ".claude/rules", kind: "dir" },
|
|
954
|
+
{ editor: "Windsurf", path: ".windsurfrules", kind: "file" },
|
|
955
|
+
{ editor: "Windsurf", path: ".windsurf/rules", kind: "dir" },
|
|
956
|
+
{ editor: "Trae", path: ".trae/rules", kind: "dir" },
|
|
957
|
+
{ editor: "Trae", path: ".trae/project_rules.md", kind: "file" },
|
|
958
|
+
{ editor: "GitHub Copilot", path: ".github/copilot-instructions.md", kind: "file" },
|
|
959
|
+
{ editor: "Cline", path: ".clinerules", kind: "file" },
|
|
960
|
+
{ editor: "Agents", path: "AGENTS.md", kind: "file" }
|
|
961
|
+
];
|
|
962
|
+
function isRuleFile(name) {
|
|
963
|
+
const lower = name.toLowerCase();
|
|
964
|
+
return lower.endsWith(".md") || lower.endsWith(".mdc");
|
|
965
|
+
}
|
|
966
|
+
async function isDir(fs, path) {
|
|
967
|
+
try {
|
|
968
|
+
await fs.readDir(path);
|
|
969
|
+
return true;
|
|
970
|
+
} catch {
|
|
971
|
+
return false;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
async function detectRuleSources(fs, workspaceRoot, repoDirs) {
|
|
975
|
+
const found = [];
|
|
976
|
+
const seen = /* @__PURE__ */ new Set();
|
|
977
|
+
const add = (repo, editor, relPath) => {
|
|
978
|
+
if (seen.has(relPath)) return;
|
|
979
|
+
seen.add(relPath);
|
|
980
|
+
found.push({ repo, editor, path: relPath });
|
|
981
|
+
};
|
|
982
|
+
for (const repo of repoDirs) {
|
|
983
|
+
const repoAbs = repo === "." ? workspaceRoot : joinPath(workspaceRoot, repo);
|
|
984
|
+
for (const conv of RULE_SOURCES) {
|
|
985
|
+
const abs = joinPath(repoAbs, conv.path);
|
|
986
|
+
const rel = repo === "." ? conv.path : joinPath(repo, conv.path);
|
|
987
|
+
if (conv.kind === "file") {
|
|
988
|
+
if (await fs.exists(abs)) add(repo, conv.editor, rel);
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
if (!await isDir(fs, abs)) continue;
|
|
992
|
+
let entries;
|
|
993
|
+
try {
|
|
994
|
+
entries = await fs.readDir(abs);
|
|
995
|
+
} catch {
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
for (const entry of entries) {
|
|
999
|
+
if (entry.isFile && isRuleFile(entry.name)) {
|
|
1000
|
+
add(repo, conv.editor, joinPath(rel, entry.name));
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return found;
|
|
1006
|
+
}
|
|
1007
|
+
async function readRuleExcerpts(fs, workspaceRoot, sources, opts = {}) {
|
|
1008
|
+
const maxPer = opts.maxCharsPerRule ?? 400;
|
|
1009
|
+
const total = opts.totalBudget ?? 1500;
|
|
1010
|
+
const out = [];
|
|
1011
|
+
let used = 0;
|
|
1012
|
+
for (const source of sources) {
|
|
1013
|
+
if (used >= total) break;
|
|
1014
|
+
let text;
|
|
1015
|
+
try {
|
|
1016
|
+
text = await fs.readFile(joinPath(workspaceRoot, source.path));
|
|
1017
|
+
} catch {
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
const budget = Math.min(maxPer, total - used);
|
|
1021
|
+
const excerpt = clampExcerpt(text.trim(), budget);
|
|
1022
|
+
used += excerpt.length;
|
|
1023
|
+
out.push({ source, excerpt });
|
|
1024
|
+
}
|
|
1025
|
+
return out;
|
|
1026
|
+
}
|
|
1027
|
+
function clampExcerpt(text, maxChars) {
|
|
1028
|
+
if (text.length <= maxChars) return text;
|
|
1029
|
+
return `${text.slice(0, Math.max(0, maxChars - 1))}\u2026`;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
923
1032
|
// src/workspace/context-builder.ts
|
|
924
1033
|
var DEFAULT_MAX_TOKENS = 2e3;
|
|
925
1034
|
var NOTE_SCAN_MAX_DEPTH = 8;
|
|
@@ -955,6 +1064,9 @@ var ContextBuilder = class {
|
|
|
955
1064
|
async build(input) {
|
|
956
1065
|
const sections2 = [this.workspaceSection(input.workspace)];
|
|
957
1066
|
let relevantNotes = [];
|
|
1067
|
+
if (input.includeRules !== false && input.workspace.ruleSources.length > 0) {
|
|
1068
|
+
sections2.push(await this.codingRulesSection(input.workspace));
|
|
1069
|
+
}
|
|
958
1070
|
if (input.vaultPath) {
|
|
959
1071
|
const notes = await this.searchNotes(input.vaultPath, input.task);
|
|
960
1072
|
relevantNotes = notes.map((n) => n.path);
|
|
@@ -964,6 +1076,22 @@ var ContextBuilder = class {
|
|
|
964
1076
|
const context = clamp(sections2.join("\n\n"), maxChars);
|
|
965
1077
|
return { context, relevantNotes };
|
|
966
1078
|
}
|
|
1079
|
+
/** Each repo's coding rules (read fresh, clamped), non-exclusive across editors. */
|
|
1080
|
+
async codingRulesSection(ws) {
|
|
1081
|
+
const excerpts = await readRuleExcerpts(this.fs, ws.root, ws.ruleSources);
|
|
1082
|
+
if (excerpts.length === 0) return "## Coding rules\n\nNo readable rule files.";
|
|
1083
|
+
const lines = [
|
|
1084
|
+
"## Coding rules",
|
|
1085
|
+
"",
|
|
1086
|
+
"Follow these per-repo rules (any editor). Read the full file for details:",
|
|
1087
|
+
""
|
|
1088
|
+
];
|
|
1089
|
+
for (const { source, excerpt } of excerpts) {
|
|
1090
|
+
lines.push(`### \`${source.path}\` \u2014 ${source.editor} (${source.repo})`);
|
|
1091
|
+
lines.push("", excerpt, "");
|
|
1092
|
+
}
|
|
1093
|
+
return lines.join("\n").trimEnd();
|
|
1094
|
+
}
|
|
967
1095
|
workspaceSection(ws) {
|
|
968
1096
|
const lines = ["## Workspace context", "", ws.description.trim()];
|
|
969
1097
|
if (ws.languages.length > 0) lines.push("", `Languages: ${ws.languages.join(", ")}`);
|
|
@@ -1161,6 +1289,30 @@ var ObsidianDetector = class {
|
|
|
1161
1289
|
}
|
|
1162
1290
|
};
|
|
1163
1291
|
|
|
1292
|
+
// src/worktree/git-repo-detector.ts
|
|
1293
|
+
async function hasGitDir(fs, dir) {
|
|
1294
|
+
try {
|
|
1295
|
+
const entries = await fs.readDir(dir);
|
|
1296
|
+
return entries.some((e) => e.name === ".git" && e.isDirectory);
|
|
1297
|
+
} catch {
|
|
1298
|
+
return false;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
async function detectGitRepos(fs, workspaceRoot) {
|
|
1302
|
+
let entries;
|
|
1303
|
+
try {
|
|
1304
|
+
entries = await fs.readDir(workspaceRoot);
|
|
1305
|
+
} catch {
|
|
1306
|
+
return [];
|
|
1307
|
+
}
|
|
1308
|
+
const repos = [];
|
|
1309
|
+
for (const entry of entries) {
|
|
1310
|
+
if (!entry.isDirectory) continue;
|
|
1311
|
+
if (await hasGitDir(fs, joinPath(workspaceRoot, entry.name))) repos.push(entry.name);
|
|
1312
|
+
}
|
|
1313
|
+
return repos.sort();
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1164
1316
|
// src/workspace/scanner.ts
|
|
1165
1317
|
var MAX_DEPTH = 6;
|
|
1166
1318
|
var DEFAULT_IGNORES = [
|
|
@@ -1230,6 +1382,10 @@ var WorkspaceScanner = class {
|
|
|
1230
1382
|
const framework = rootProject?.framework ?? projects.find((p) => p.framework)?.framework;
|
|
1231
1383
|
const conventions = await this.detectConventions(root);
|
|
1232
1384
|
const description = await this.detectDescription(root, rootProject, framework, languages);
|
|
1385
|
+
const repoDirs = [
|
|
1386
|
+
.../* @__PURE__ */ new Set([".", ...await detectGitRepos(this.fs, root), ...projects.map((p) => p.path)])
|
|
1387
|
+
];
|
|
1388
|
+
const ruleSources = await detectRuleSources(this.fs, root, repoDirs);
|
|
1233
1389
|
return {
|
|
1234
1390
|
root,
|
|
1235
1391
|
description,
|
|
@@ -1237,6 +1393,7 @@ var WorkspaceScanner = class {
|
|
|
1237
1393
|
framework,
|
|
1238
1394
|
conventions,
|
|
1239
1395
|
projects,
|
|
1396
|
+
ruleSources,
|
|
1240
1397
|
folderMap: [...acc.topDirs].sort()
|
|
1241
1398
|
};
|
|
1242
1399
|
}
|
|
@@ -1396,10 +1553,10 @@ function globToRegExp(glob) {
|
|
|
1396
1553
|
}
|
|
1397
1554
|
return out;
|
|
1398
1555
|
}
|
|
1399
|
-
function isIgnored(rules, relPath,
|
|
1556
|
+
function isIgnored(rules, relPath, isDir2) {
|
|
1400
1557
|
const base = basename(relPath);
|
|
1401
1558
|
for (const rule of rules) {
|
|
1402
|
-
if (rule.dirOnly && !
|
|
1559
|
+
if (rule.dirOnly && !isDir2) continue;
|
|
1403
1560
|
const target = rule.anchored ? relPath : base;
|
|
1404
1561
|
if (rule.re.test(target)) return true;
|
|
1405
1562
|
}
|
|
@@ -1563,30 +1720,6 @@ var GitDetector = class {
|
|
|
1563
1720
|
}
|
|
1564
1721
|
};
|
|
1565
1722
|
|
|
1566
|
-
// src/worktree/git-repo-detector.ts
|
|
1567
|
-
async function hasGitDir(fs, dir) {
|
|
1568
|
-
try {
|
|
1569
|
-
const entries = await fs.readDir(dir);
|
|
1570
|
-
return entries.some((e) => e.name === ".git" && e.isDirectory);
|
|
1571
|
-
} catch {
|
|
1572
|
-
return false;
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
async function detectGitRepos(fs, workspaceRoot) {
|
|
1576
|
-
let entries;
|
|
1577
|
-
try {
|
|
1578
|
-
entries = await fs.readDir(workspaceRoot);
|
|
1579
|
-
} catch {
|
|
1580
|
-
return [];
|
|
1581
|
-
}
|
|
1582
|
-
const repos = [];
|
|
1583
|
-
for (const entry of entries) {
|
|
1584
|
-
if (!entry.isDirectory) continue;
|
|
1585
|
-
if (await hasGitDir(fs, joinPath(workspaceRoot, entry.name))) repos.push(entry.name);
|
|
1586
|
-
}
|
|
1587
|
-
return repos.sort();
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
1723
|
// src/worktree/manager.ts
|
|
1591
1724
|
var WorktreeError = class extends Error {
|
|
1592
1725
|
name = "WorktreeError";
|
|
@@ -1789,6 +1922,7 @@ export {
|
|
|
1789
1922
|
ObsidianDetector,
|
|
1790
1923
|
REASONING_PRIORITIES,
|
|
1791
1924
|
ROLE_DESCRIPTIONS,
|
|
1925
|
+
RULE_SOURCES,
|
|
1792
1926
|
Reviewer,
|
|
1793
1927
|
VAULT_NOTES_DIR,
|
|
1794
1928
|
VaultWriter,
|
|
@@ -1805,6 +1939,7 @@ export {
|
|
|
1805
1939
|
configFileSchema,
|
|
1806
1940
|
copyEnvFiles,
|
|
1807
1941
|
detectGitRepos,
|
|
1942
|
+
detectRuleSources,
|
|
1808
1943
|
diffLines,
|
|
1809
1944
|
dirname,
|
|
1810
1945
|
editorWorkspaceContent,
|
|
@@ -1819,6 +1954,7 @@ export {
|
|
|
1819
1954
|
normalizeVaultPath,
|
|
1820
1955
|
parseChambaConfig,
|
|
1821
1956
|
planWorktrees,
|
|
1957
|
+
readRuleExcerpts,
|
|
1822
1958
|
renderNote,
|
|
1823
1959
|
renderWorkspaceMarkdown,
|
|
1824
1960
|
resolveEffort,
|
package/package.json
CHANGED