@bcelep/capint 0.4.2

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.
Files changed (69) hide show
  1. package/AGENT.md +28 -0
  2. package/CHANGELOG.md +58 -0
  3. package/README.md +94 -0
  4. package/bin/capint.js +90 -0
  5. package/design.md +23 -0
  6. package/docs/architecture-decisions.md +95 -0
  7. package/docs/execution-intent-contract.md +81 -0
  8. package/docs/manifest-schema.md +36 -0
  9. package/docs/release-checklist.md +31 -0
  10. package/package.json +33 -0
  11. package/projections/session-start.md +32 -0
  12. package/registry.json +12 -0
  13. package/scripts/release-check.mjs +40 -0
  14. package/scripts/validate-matrix.mjs +83 -0
  15. package/skill-routing-matrix.json +150 -0
  16. package/skills/capability-router/SKILL.md +3 -0
  17. package/skills/context-memory-bridge/SKILL.md +17 -0
  18. package/skills/localization-hub/SKILL.md +17 -0
  19. package/skills/refactor/SKILL.md +17 -0
  20. package/skills/systematic-debugging/SKILL.md +19 -0
  21. package/src/commands/audit.js +32 -0
  22. package/src/commands/consult.js +64 -0
  23. package/src/commands/doctor.js +36 -0
  24. package/src/commands/ide.js +62 -0
  25. package/src/commands/init.js +50 -0
  26. package/src/commands/memory.js +60 -0
  27. package/src/commands/route.js +30 -0
  28. package/src/commands/scaffold.js +37 -0
  29. package/src/commands/status.js +22 -0
  30. package/src/commands/uninstall.js +46 -0
  31. package/src/commands/upgrade.js +69 -0
  32. package/src/lib/audit.js +107 -0
  33. package/src/lib/capability-router.js +118 -0
  34. package/src/lib/context-memory-bridge.js +87 -0
  35. package/src/lib/context-pack.js +39 -0
  36. package/src/lib/contract.js +71 -0
  37. package/src/lib/doctor.js +115 -0
  38. package/src/lib/event-log.js +40 -0
  39. package/src/lib/execution-policy.js +168 -0
  40. package/src/lib/ide-sync.js +277 -0
  41. package/src/lib/intent-parser.js +116 -0
  42. package/src/lib/orchestration.js +25 -0
  43. package/src/lib/providers/activation-policy.js +93 -0
  44. package/src/lib/providers/graph-provider.js +35 -0
  45. package/src/lib/providers/local-graph-adapter.js +40 -0
  46. package/src/lib/providers/local-memory-adapter.js +41 -0
  47. package/src/lib/providers/memory-provider.js +35 -0
  48. package/src/lib/route-engine.js +191 -0
  49. package/src/lib/scaffold/file-policy.js +92 -0
  50. package/src/lib/scaffold/index.js +29 -0
  51. package/src/lib/scaffold/manifest-schema.js +116 -0
  52. package/src/lib/scaffold/manifest.js +34 -0
  53. package/src/lib/scaffold/presets.js +132 -0
  54. package/src/lib/uninstall.js +126 -0
  55. package/src/lib/upgrade-matrix.js +120 -0
  56. package/templates/bundle/skills/capability-router/SKILL.md +12 -0
  57. package/templates/bundle/skills/context-memory-bridge/SKILL.md +17 -0
  58. package/templates/bundle/skills/localization-hub/SKILL.md +17 -0
  59. package/templates/bundle/skills/refactor/SKILL.md +17 -0
  60. package/templates/bundle/skills/systematic-debugging/SKILL.md +19 -0
  61. package/templates/bundle/workflows/forge.md +51 -0
  62. package/templates/minimal/.capint/rules/core.md +18 -0
  63. package/templates/minimal/AGENT.md +26 -0
  64. package/templates/minimal/AGENTS.md +9 -0
  65. package/templates/minimal/design.md +25 -0
  66. package/templates/minimal/registry.json +7 -0
  67. package/templates/prismx-compatible/.capint/context.json +8 -0
  68. package/templates/prismx-compatible/HANDOFF.md +19 -0
  69. package/workflows/forge.md +51 -0
@@ -0,0 +1,29 @@
1
+ const { applyFilePolicy, buildReport } = require("./file-policy");
2
+ const { getPresetFiles, PRESET_MANIFEST, copyBundle } = require("./presets");
3
+ const { writeScaffoldManifest } = require("./manifest");
4
+
5
+ function runScaffold({ rootDir, preset = "minimal", projectName = "project", force = false }) {
6
+ if (!PRESET_MANIFEST[preset]) {
7
+ return { error: `unknown preset: ${preset}. Available: ${Object.keys(PRESET_MANIFEST).join(", ")}` };
8
+ }
9
+
10
+ const files = getPresetFiles(preset, {
11
+ project_name: projectName,
12
+ date: new Date().toISOString().slice(0, 10)
13
+ });
14
+
15
+ const results = [];
16
+ for (const [rel, content] of Object.entries(files)) {
17
+ results.push(applyFilePolicy({ rootDir, relativePath: rel, content, force }));
18
+ }
19
+
20
+ results.push(...copyBundle({ rootDir, force }));
21
+
22
+ const report = buildReport(results);
23
+ report.preset = preset;
24
+ report.manifest = PRESET_MANIFEST[preset];
25
+ report.manifest_path = writeScaffoldManifest(rootDir, report);
26
+ return report;
27
+ }
28
+
29
+ module.exports = { runScaffold, PRESET_MANIFEST };
@@ -0,0 +1,116 @@
1
+ const MANIFEST_SCHEMA_VERSION = "2.0";
2
+
3
+ const ACTIONS = new Set(["created", "updated", "skipped", "conflict"]);
4
+ const SOURCES = new Set(["capint", "user"]);
5
+ const KINDS = new Set(["doc", "skill", "workflow", "matrix", "registry", "ide_projection"]);
6
+
7
+ function inferKindFromPath(relPath) {
8
+ const p = relPath.replace(/\\/g, "/");
9
+ if (p.startsWith("skills/")) return "skill";
10
+ if (p.startsWith("workflows/")) return "workflow";
11
+ if (p === "skill-routing-matrix.json") return "matrix";
12
+ if (p === "registry.json") return "registry";
13
+ if (
14
+ p.includes(".cursor/rules/") ||
15
+ p === "CLAUDE.md" ||
16
+ p === "GEMINI.md" ||
17
+ p.startsWith(".agents/")
18
+ ) {
19
+ return "ide_projection";
20
+ }
21
+ return "doc";
22
+ }
23
+
24
+ function defaultFileMeta(relPath, overrides = {}) {
25
+ const kind = overrides.kind || inferKindFromPath(relPath);
26
+ const source = overrides.source || "capint";
27
+ const removable =
28
+ typeof overrides.removable === "boolean"
29
+ ? overrides.removable
30
+ : source === "capint" && kind !== "doc";
31
+ return {
32
+ path: relPath.replace(/\\/g, "/"),
33
+ source,
34
+ removable,
35
+ kind,
36
+ marker: overrides.marker ?? null
37
+ };
38
+ }
39
+
40
+ function enrichFileEntry(entry) {
41
+ const pathNorm = (entry.path || "").replace(/\\/g, "/");
42
+ const meta = defaultFileMeta(pathNorm, {
43
+ source: entry.source,
44
+ removable: entry.removable,
45
+ kind: entry.kind,
46
+ marker: entry.marker
47
+ });
48
+ return {
49
+ path: pathNorm,
50
+ action: entry.action,
51
+ source: meta.source,
52
+ removable: meta.removable,
53
+ kind: meta.kind,
54
+ marker: meta.marker,
55
+ sidecar: entry.sidecar || null
56
+ };
57
+ }
58
+
59
+ function validateManifest(manifest) {
60
+ const errors = [];
61
+ if (!manifest || typeof manifest !== "object") {
62
+ return { valid: false, errors: ["manifest must be an object"] };
63
+ }
64
+ if (manifest.schema_version !== MANIFEST_SCHEMA_VERSION) {
65
+ errors.push(`schema_version must be ${MANIFEST_SCHEMA_VERSION}`);
66
+ }
67
+ if (!Array.isArray(manifest.files)) {
68
+ errors.push("files must be an array");
69
+ return { valid: false, errors };
70
+ }
71
+ for (let i = 0; i < manifest.files.length; i++) {
72
+ const f = manifest.files[i];
73
+ if (!f.path || typeof f.path !== "string") errors.push(`files[${i}].path required`);
74
+ if (!ACTIONS.has(f.action)) errors.push(`files[${i}].action invalid: ${f.action}`);
75
+ if (!SOURCES.has(f.source)) errors.push(`files[${i}].source invalid: ${f.source}`);
76
+ if (typeof f.removable !== "boolean") errors.push(`files[${i}].removable must be boolean`);
77
+ if (!KINDS.has(f.kind)) errors.push(`files[${i}].kind invalid: ${f.kind}`);
78
+ }
79
+ return { valid: errors.length === 0, errors };
80
+ }
81
+
82
+ function buildManifestDocument({ report, previous = null }) {
83
+ const files = (report.results || []).map(enrichFileEntry);
84
+ const manifest = {
85
+ schema_version: MANIFEST_SCHEMA_VERSION,
86
+ last_run: report.generated_at,
87
+ preset: report.preset,
88
+ summary: report.summary,
89
+ files,
90
+ history: [
91
+ ...(previous?.history || []).slice(-9),
92
+ {
93
+ at: report.generated_at,
94
+ preset: report.preset,
95
+ summary: report.summary
96
+ }
97
+ ]
98
+ };
99
+ const validation = validateManifest(manifest);
100
+ if (!validation.valid) {
101
+ throw new Error(`manifest validation failed: ${validation.errors.join("; ")}`);
102
+ }
103
+ return manifest;
104
+ }
105
+
106
+ module.exports = {
107
+ MANIFEST_SCHEMA_VERSION,
108
+ ACTIONS,
109
+ SOURCES,
110
+ KINDS,
111
+ inferKindFromPath,
112
+ defaultFileMeta,
113
+ enrichFileEntry,
114
+ validateManifest,
115
+ buildManifestDocument
116
+ };
@@ -0,0 +1,34 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { buildManifestDocument } = require("./manifest-schema");
4
+
5
+ function writeScaffoldManifest(rootDir, report) {
6
+ const capintDir = path.join(rootDir, ".capint");
7
+ if (!fs.existsSync(capintDir)) fs.mkdirSync(capintDir, { recursive: true });
8
+
9
+ const manifestPath = path.join(capintDir, "scaffold-manifest.json");
10
+ let previous = null;
11
+ if (fs.existsSync(manifestPath)) {
12
+ try {
13
+ previous = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
14
+ } catch {
15
+ previous = null;
16
+ }
17
+ }
18
+
19
+ const manifest = buildManifestDocument({ report, previous });
20
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf-8");
21
+ return manifestPath;
22
+ }
23
+
24
+ function readScaffoldManifest(rootDir) {
25
+ const manifestPath = path.join(rootDir, ".capint", "scaffold-manifest.json");
26
+ if (!fs.existsSync(manifestPath)) return null;
27
+ try {
28
+ return JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+
34
+ module.exports = { writeScaffoldManifest, readScaffoldManifest };
@@ -0,0 +1,132 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { applyFilePolicy } = require("./file-policy");
4
+
5
+ function loadTemplate(name) {
6
+ const tplDir = path.join(__dirname, "..", "..", "..", "templates", name);
7
+ const files = {};
8
+ if (!fs.existsSync(tplDir)) return files;
9
+ for (const rel of walk(tplDir)) {
10
+ const key = rel.replace(/\\/g, "/");
11
+ files[key] = fs.readFileSync(path.join(tplDir, rel), "utf-8");
12
+ }
13
+ return files;
14
+ }
15
+
16
+ function walk(dir, base = "") {
17
+ const out = [];
18
+ for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
19
+ const rel = base ? `${base}/${ent.name}` : ent.name;
20
+ if (ent.isDirectory()) out.push(...walk(path.join(dir, ent.name), rel));
21
+ else out.push(rel);
22
+ }
23
+ return out;
24
+ }
25
+
26
+ function interpolate(content, vars) {
27
+ let out = content;
28
+ for (const [k, v] of Object.entries(vars)) {
29
+ out = out.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v);
30
+ }
31
+ return out;
32
+ }
33
+
34
+ function deriveRegistryFromMatrix(matrix) {
35
+ const core = new Set(["capability-router"]);
36
+ const recommended = new Set();
37
+
38
+ for (const tt of matrix?.task_types || []) {
39
+ for (const s of tt.skills || []) {
40
+ if (s === "capability-router") core.add(s);
41
+ else recommended.add(s);
42
+ }
43
+ }
44
+ for (const meta of Object.values(matrix?.rules_domain_map || {})) {
45
+ for (const s of meta.skills || []) {
46
+ if (s === "capability-router") core.add(s);
47
+ else recommended.add(s);
48
+ }
49
+ }
50
+ recommended.delete("capability-router");
51
+
52
+ return {
53
+ schema_version: "1.0",
54
+ core: [...core].sort(),
55
+ recommended: [...recommended].sort(),
56
+ optional: [],
57
+ project_added: []
58
+ };
59
+ }
60
+
61
+ function getPresetFiles(preset, vars = {}) {
62
+ const pkgRoot = path.join(__dirname, "..", "..", "..");
63
+ const matrixRaw = fs.readFileSync(path.join(pkgRoot, "skill-routing-matrix.json"), "utf-8");
64
+ const matrix = JSON.parse(matrixRaw);
65
+ const registryJson = `${JSON.stringify(deriveRegistryFromMatrix(matrix), null, 2)}\n`;
66
+ const routingBundle = {
67
+ "skill-routing-matrix.json": matrixRaw,
68
+ "registry.json": registryJson
69
+ };
70
+
71
+ const presets = {
72
+ minimal: () => ({ ...loadTemplate("minimal"), ...routingBundle }),
73
+ "prismx-compatible": () => {
74
+ const minimal = loadTemplate("minimal");
75
+ const extra = loadTemplate("prismx-compatible");
76
+ return { ...minimal, ...extra, ...routingBundle };
77
+ }
78
+ };
79
+ const loader = presets[preset] || presets.minimal;
80
+ const raw = loader();
81
+ const files = {};
82
+ for (const [rel, content] of Object.entries(raw)) {
83
+ files[rel] = interpolate(content, vars);
84
+ }
85
+ return files;
86
+ }
87
+
88
+ function copyBundle({ rootDir, force = false }) {
89
+ const bundleDir = path.join(__dirname, "..", "..", "..", "templates", "bundle");
90
+ const results = [];
91
+ if (!fs.existsSync(bundleDir)) return results;
92
+
93
+ for (const rel of walk(bundleDir)) {
94
+ const relNorm = rel.replace(/\\/g, "/");
95
+ const content = fs.readFileSync(path.join(bundleDir, rel), "utf-8");
96
+ results.push(
97
+ applyFilePolicy({
98
+ rootDir,
99
+ relativePath: relNorm,
100
+ content,
101
+ force
102
+ })
103
+ );
104
+ }
105
+ return results;
106
+ }
107
+
108
+ const PRESET_MANIFEST = {
109
+ minimal: {
110
+ id: "minimal",
111
+ description: "AGENT.md, design.md, rules, matrix, registry, skill bundle",
112
+ files: [
113
+ "AGENT.md",
114
+ "AGENTS.md",
115
+ "design.md",
116
+ ".capint/rules/core.md",
117
+ ".gitignore",
118
+ "skill-routing-matrix.json",
119
+ "registry.json",
120
+ "skills/",
121
+ "workflows/"
122
+ ]
123
+ },
124
+ "prismx-compatible": {
125
+ id: "prismx-compatible",
126
+ description: "minimal + HANDOFF.md + .capint/context.json",
127
+ extends: "minimal",
128
+ files: ["HANDOFF.md", ".capint/context.json"]
129
+ }
130
+ };
131
+
132
+ module.exports = { getPresetFiles, PRESET_MANIFEST, interpolate, deriveRegistryFromMatrix, copyBundle };
@@ -0,0 +1,126 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { CAPINT_SYNC_MARKER } = require("./ide-sync");
4
+ const { readScaffoldManifest } = require("./scaffold/manifest");
5
+
6
+ function isCapintMarked(filePath) {
7
+ if (!fs.existsSync(filePath)) return false;
8
+ try {
9
+ return fs.readFileSync(filePath, "utf-8").includes(CAPINT_SYNC_MARKER);
10
+ } catch {
11
+ return false;
12
+ }
13
+ }
14
+
15
+ function collectManifestRemovals(rootDir, manifest, options = {}) {
16
+ const plan = [];
17
+ const { keepAgent = false, keepIde = false, includeSidecars = false } = options;
18
+
19
+ for (const entry of manifest?.files || []) {
20
+ if (entry.source !== "capint" || !entry.removable) continue;
21
+ if (entry.kind === "ide_projection" && keepIde) continue;
22
+ if (entry.path === "AGENT.md" && keepAgent) continue;
23
+ if (entry.path === "design.md" && keepAgent) continue;
24
+ if (entry.path === "AGENTS.md" && keepAgent) continue;
25
+
26
+ const abs = path.join(rootDir, entry.path);
27
+ if (entry.kind === "ide_projection" && !isCapintMarked(abs)) continue;
28
+ if (fs.existsSync(abs)) {
29
+ plan.push({ path: entry.path, abs, type: "file", kind: entry.kind });
30
+ }
31
+ }
32
+
33
+ if (includeSidecars) {
34
+ for (const entry of manifest?.files || []) {
35
+ if (entry.sidecar) {
36
+ const abs = path.join(rootDir, entry.sidecar);
37
+ if (fs.existsSync(abs)) plan.push({ path: entry.sidecar, abs, type: "sidecar" });
38
+ }
39
+ }
40
+ for (const ent of fs.readdirSync(rootDir)) {
41
+ if (ent.endsWith(".capint.new.md")) {
42
+ plan.push({ path: ent, abs: path.join(rootDir, ent), type: "sidecar" });
43
+ }
44
+ }
45
+ }
46
+
47
+ const capintDir = path.join(rootDir, ".capint");
48
+ if (fs.existsSync(capintDir)) {
49
+ plan.push({ path: ".capint/", abs: capintDir, type: "dir" });
50
+ }
51
+
52
+ const seen = new Set();
53
+ return plan.filter((p) => {
54
+ if (seen.has(p.abs)) return false;
55
+ seen.add(p.abs);
56
+ return true;
57
+ });
58
+ }
59
+
60
+ function collectIdeFallbackRemovals(rootDir, keepIde) {
61
+ if (keepIde) return [];
62
+ const candidates = [
63
+ path.join(rootDir, ".cursor", "rules", "00-capint-session.mdc"),
64
+ path.join(rootDir, "CLAUDE.md"),
65
+ path.join(rootDir, "GEMINI.md"),
66
+ path.join(rootDir, ".agents", "rules", "00-capint-session.md"),
67
+ path.join(rootDir, ".agents", "CAPINT.md"),
68
+ path.join(rootDir, ".agents", "skills", "capint-router", "SKILL.md")
69
+ ];
70
+ return candidates
71
+ .filter((abs) => isCapintMarked(abs))
72
+ .map((abs) => ({
73
+ path: path.relative(rootDir, abs).replace(/\\/g, "/"),
74
+ abs,
75
+ type: "file",
76
+ kind: "ide_projection"
77
+ }));
78
+ }
79
+
80
+ function buildUninstallPlan(rootDir, options = {}) {
81
+ const manifest = readScaffoldManifest(rootDir);
82
+ const fromManifest = manifest
83
+ ? collectManifestRemovals(rootDir, manifest, options)
84
+ : [];
85
+ const fromIde = collectIdeFallbackRemovals(rootDir, options.keepIde);
86
+ const merged = [...fromManifest, ...fromIde];
87
+ const seen = new Set();
88
+ return merged.filter((p) => {
89
+ if (seen.has(p.abs)) return false;
90
+ seen.add(p.abs);
91
+ return true;
92
+ });
93
+ }
94
+
95
+ function removePath(entry) {
96
+ if (entry.type === "dir") {
97
+ fs.rmSync(entry.abs, { recursive: true, force: true });
98
+ return;
99
+ }
100
+ fs.rmSync(entry.abs, { force: true });
101
+ }
102
+
103
+ function executeUninstall(rootDir, plan, dryRun) {
104
+ const removed = [];
105
+ const skipped = [];
106
+ for (const entry of plan) {
107
+ if (!fs.existsSync(entry.abs)) {
108
+ skipped.push(entry.path);
109
+ continue;
110
+ }
111
+ if (entry.kind === "ide_projection" && !isCapintMarked(entry.abs)) {
112
+ skipped.push(entry.path);
113
+ continue;
114
+ }
115
+ if (!dryRun) removePath(entry);
116
+ removed.push(entry.path);
117
+ }
118
+ return { removed, skipped };
119
+ }
120
+
121
+ module.exports = {
122
+ buildUninstallPlan,
123
+ executeUninstall,
124
+ isCapintMarked,
125
+ collectManifestRemovals
126
+ };
@@ -0,0 +1,120 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ function loadJson(filePath) {
5
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
6
+ }
7
+
8
+ function taskTypeIds(matrix) {
9
+ return new Set((matrix?.task_types || []).map((t) => t.id));
10
+ }
11
+
12
+ function mergeTaskTypes(packageMatrix, projectMatrix) {
13
+ const conflicts = [];
14
+ const customPreserved = [];
15
+ const pkgById = new Map((packageMatrix.task_types || []).map((t) => [t.id, t]));
16
+ const projById = new Map((projectMatrix.task_types || []).map((t) => [t.id, t]));
17
+
18
+ for (const [id, proj] of projById) {
19
+ if (!pkgById.has(id)) {
20
+ customPreserved.push(id);
21
+ }
22
+ }
23
+
24
+ const merged = [];
25
+ for (const pkgTt of packageMatrix.task_types || []) {
26
+ const projTt = projById.get(pkgTt.id);
27
+ if (!projTt) {
28
+ merged.push(structuredClone(pkgTt));
29
+ continue;
30
+ }
31
+ const out = structuredClone(pkgTt);
32
+ for (const key of Object.keys(projTt)) {
33
+ if (key === "id") continue;
34
+ const pv = projTt[key];
35
+ const pkgv = pkgTt[key];
36
+ if (JSON.stringify(pv) !== JSON.stringify(pkgv)) {
37
+ conflicts.push({
38
+ id: pkgTt.id,
39
+ field: key,
40
+ project_value: pv,
41
+ package_value: pkgv,
42
+ resolution: "package_wins"
43
+ });
44
+ }
45
+ }
46
+ merged.push(out);
47
+ }
48
+
49
+ for (const id of customPreserved) {
50
+ merged.push(structuredClone(projById.get(id)));
51
+ }
52
+
53
+ return { merged, conflicts, custom_preserved: customPreserved };
54
+ }
55
+
56
+ function mergeMatrices(packageMatrix, projectMatrix) {
57
+ const topKeys = new Set([...Object.keys(packageMatrix), ...Object.keys(projectMatrix)]);
58
+ const conflicts = [];
59
+ const result = structuredClone(packageMatrix);
60
+
61
+ for (const key of topKeys) {
62
+ if (key === "task_types") continue;
63
+ if (!(key in projectMatrix)) continue;
64
+ if (JSON.stringify(projectMatrix[key]) !== JSON.stringify(packageMatrix[key])) {
65
+ conflicts.push({
66
+ id: "_top_level",
67
+ field: key,
68
+ project_value: projectMatrix[key],
69
+ package_value: packageMatrix[key],
70
+ resolution: "package_wins"
71
+ });
72
+ }
73
+ }
74
+
75
+ const tt = mergeTaskTypes(packageMatrix, projectMatrix);
76
+ result.task_types = tt.merged;
77
+ return {
78
+ merged: result,
79
+ conflicts: [...conflicts, ...tt.conflicts],
80
+ custom_preserved: tt.custom_preserved
81
+ };
82
+ }
83
+
84
+ function planUpgrade(rootDir, packageRoot) {
85
+ const projectPath = path.join(rootDir, "skill-routing-matrix.json");
86
+ const packagePath = path.join(packageRoot, "skill-routing-matrix.json");
87
+
88
+ if (!fs.existsSync(projectPath)) {
89
+ return { error: "skill-routing-matrix.json not found in project" };
90
+ }
91
+ if (!fs.existsSync(packagePath)) {
92
+ return { error: "package skill-routing-matrix.json not found" };
93
+ }
94
+
95
+ const projectMatrix = loadJson(projectPath);
96
+ const packageMatrix = loadJson(packagePath);
97
+ const { merged, conflicts, custom_preserved } = mergeMatrices(packageMatrix, projectMatrix);
98
+
99
+ return {
100
+ project_schema: projectMatrix.schema_version,
101
+ package_schema: packageMatrix.schema_version,
102
+ conflicts,
103
+ custom_preserved,
104
+ merged,
105
+ summary: `${conflicts.length} conflicts — package wins; review before --apply`
106
+ };
107
+ }
108
+
109
+ function applyUpgrade(rootDir, merged) {
110
+ const projectPath = path.join(rootDir, "skill-routing-matrix.json");
111
+ const backupDir = path.join(rootDir, ".capint", "backups");
112
+ fs.mkdirSync(backupDir, { recursive: true });
113
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
114
+ const backupPath = path.join(backupDir, `skill-routing-matrix.${stamp}.json`);
115
+ fs.copyFileSync(projectPath, backupPath);
116
+ fs.writeFileSync(projectPath, `${JSON.stringify(merged, null, 2)}\n`, "utf-8");
117
+ return backupPath;
118
+ }
119
+
120
+ module.exports = { planUpgrade, applyUpgrade, mergeMatrices };
@@ -0,0 +1,12 @@
1
+ # capability-router
2
+
3
+ Routes normalized intent to capabilities/providers using `skill-routing-matrix.json`.
4
+
5
+ ## When
6
+
7
+ - Ambiguous requests with no clear task type
8
+ - Fallback when no keyword match
9
+
10
+ ## CapInt
11
+
12
+ Default fallback skill · CLI: `capint route "<task>"`
@@ -0,0 +1,17 @@
1
+ # context-memory-bridge
2
+
3
+ Use when the task requires prior decisions, handoff context, or session history lookup.
4
+
5
+ ## When
6
+
7
+ - memory lookup, what did we decide, geçen karar, history
8
+
9
+ ## Steps
10
+
11
+ 1. Read HANDOFF.md, AGENT.md, design.md when present
12
+ 2. Summarize relevant prior decisions
13
+ 3. Do not invent history; cite file paths
14
+
15
+ ## CapInt
16
+
17
+ Matrix capability: `memory-retrieval` · Memory: required
@@ -0,0 +1,17 @@
1
+ # localization-hub
2
+
3
+ Use for i18n, translation files, locale keys, and language hub tasks.
4
+
5
+ ## When
6
+
7
+ - User mentions i18n, localization, çeviri, lang file, translation hub
8
+
9
+ ## Steps
10
+
11
+ 1. Identify target locales and key namespaces
12
+ 2. Check existing translation patterns in the repo
13
+ 3. Apply consistent key naming; avoid hardcoded strings
14
+
15
+ ## CapInt
16
+
17
+ Matrix capability: `localization-hub`
@@ -0,0 +1,17 @@
1
+ # refactor
2
+
3
+ Use when simplifying code, reducing smell, or cleaning structure without changing behavior.
4
+
5
+ ## When
6
+
7
+ - refactor, clean up, simplify, code smell
8
+
9
+ ## Steps
10
+
11
+ 1. Ensure tests exist or add minimal coverage
12
+ 2. Make smallest structural change
13
+ 3. Re-run tests; no behavior change unless intended
14
+
15
+ ## CapInt
16
+
17
+ Matrix capability: `refactor-simplify` · Workflow: `/forge`
@@ -0,0 +1,19 @@
1
+ # systematic-debugging
2
+
3
+ Use when fixing bugs, errors, or unexpected behavior before proposing fixes.
4
+
5
+ ## When
6
+
7
+ - User reports broken behavior, test failures, or regressions
8
+ - Keywords: bug, error, fix, hata, çalışmıyor
9
+
10
+ ## Steps
11
+
12
+ 1. Reproduce the issue with minimal steps
13
+ 2. Gather evidence (logs, stack traces)
14
+ 3. Form hypothesis; test one change at a time
15
+ 4. Verify fix with targeted test
16
+
17
+ ## CapInt
18
+
19
+ Matrix capability: `systematic-debugging` · Workflow: `/forge`
@@ -0,0 +1,51 @@
1
+ # /forge
2
+
3
+ Default implementation workflow for medium/heavy tasks after confirm.
4
+
5
+ Inspired by harness-style phased orchestration; CapInt router selects this workflow for debug/refactor capabilities.
6
+
7
+ ## Execution modes (from confirm option)
8
+
9
+ | Confirm option | Mode | Behavior |
10
+ |----------------|------|----------|
11
+ | `apply_now` | subagent | Single focused pass, minimal ceremony |
12
+ | `plan_first` | pipeline | Phased plan before edits |
13
+ | `analyze_only` | explore | Read-only analysis, no file writes |
14
+
15
+ ## Phase 0 — Context
16
+
17
+ 1. Read `AGENT.md`, `design.md`, optional `HANDOFF.md`
18
+ 2. Use `context_pack` from Execution Intent when memory is optional/required
19
+ 3. If ambiguous, stop and ask one clarifying question
20
+
21
+ ## Phase 1 — Intent lock
22
+
23
+ 1. Restate capability + resolution from Execution Intent
24
+ 2. List assumptions and success criteria
25
+ 3. Wait for confirm unless `apply_now` on light tasks
26
+
27
+ ## Phase 2 — Execute
28
+
29
+ 1. Apply smallest correct change set
30
+ 2. Match project conventions; no drive-by refactors
31
+ 3. For `producer_reviewer` pattern: implement then self-review before claiming done
32
+
33
+ ## Phase 3 — Verify
34
+
35
+ 1. Run verification hints from Execution Intent
36
+ 2. Evidence before claims — no "done" without command output
37
+ 3. If verification fails, return to Phase 2 (max 2 retries)
38
+
39
+ ## Phase 4 — Handoff
40
+
41
+ 1. Summarize what changed and why
42
+ 2. Note follow-on capabilities from `orchestration.chains` if any
43
+ 3. Update `HANDOFF.md` when session material decisions were made
44
+
45
+ ## Error handling
46
+
47
+ | Situation | Action |
48
+ |-----------|--------|
49
+ | Blocked on missing info | `NEEDS_CONTEXT` — ask user, do not guess |
50
+ | Verification failed | Fix and re-verify; do not skip |
51
+ | Scope creep detected | Split task; route again via `capint consult` |