@bcelep/capint 0.6.5 → 0.7.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/AGENT.md +1 -1
- package/CHANGELOG.md +23 -0
- package/bin/capint.js +11 -2
- package/docs/PRD-capint.md +94 -0
- package/docs/PRD-v0.5-agent-capability-activation.md +10 -1
- package/docs/architecture-decisions.md +2 -0
- package/docs/capint-rehber.md +439 -0
- package/docs/capint-stack.md +118 -0
- package/docs/community.md +19 -0
- package/docs/conventions/daily-use.md +15 -1
- package/docs/eval/beginner-test-protocol.md +39 -0
- package/docs/eval/survey-template.md +16 -0
- package/docs/generated/README.md +6 -0
- package/docs/generated/execution-intent.v1.md +211 -0
- package/docs/generated/explanation.v1.md +127 -0
- package/docs/generated/explanation.v2.md +142 -0
- package/docs/generated/ui-api.v1.md +178 -0
- package/docs/maintainer-dogfood.md +3 -2
- package/docs/recipes/memory-graph-pairing.md +200 -0
- package/docs/skill-audit-runbook.md +32 -0
- package/locales/en.json +27 -0
- package/locales/tr.json +27 -0
- package/package.json +4 -2
- package/projections/session-start.md +14 -2
- package/schemas/execution-intent.v1.json +69 -0
- package/schemas/explanation.v2.json +51 -0
- package/schemas/ui-api.v1.json +85 -0
- package/scripts/generate-schema-docs.mjs +104 -0
- package/scripts/release-check.mjs +10 -0
- package/skills/prismx-skill-gateway/SKILL.md +10 -3
- package/src/commands/doctor.js +9 -0
- package/src/commands/feedback.js +29 -0
- package/src/commands/init.js +12 -0
- package/src/commands/providers.js +54 -0
- package/src/commands/skill.js +27 -0
- package/src/commands/stack.js +97 -0
- package/src/lib/audit.js +9 -1
- package/src/lib/contract.js +9 -0
- package/src/lib/doctor.js +69 -3
- package/src/lib/execution-policy.js +3 -1
- package/src/lib/explanation-plain.js +72 -0
- package/src/lib/explanation.js +26 -2
- package/src/lib/feedback.js +65 -0
- package/src/lib/i18n.js +43 -0
- package/src/lib/providers/doctor.js +205 -0
- package/src/lib/scaffold/index.js +2 -1
- package/src/lib/scaffold/presets.js +20 -0
- package/src/lib/skill-audit.js +141 -0
- package/src/lib/stack/index.js +259 -0
- package/src/lib/ui-server.js +92 -5
- package/templates/minimal/DONE.md +10 -0
- package/templates/minimal/GUNLUK.md +15 -1
- package/templates/minimal/docs/conventions/daily-use.md +3 -1
- package/ui/app.js +100 -10
- package/ui/index.html +22 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://capint.dev/schemas/ui-api.v1.json",
|
|
4
|
+
"title": "CapInt UI Wizard API",
|
|
5
|
+
"definitions": {
|
|
6
|
+
"RouteRequest": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"required": ["task"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"task": { "type": "string", "minLength": 1 },
|
|
11
|
+
"locale": { "enum": ["tr", "en"] }
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"RouteResponse": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"required": ["result", "text_report", "chat_copy"],
|
|
17
|
+
"properties": {
|
|
18
|
+
"result": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"required": ["execution_intent"],
|
|
21
|
+
"properties": {
|
|
22
|
+
"execution_intent": { "$ref": "execution-intent.v1.json" }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"text_report": { "type": "string" },
|
|
26
|
+
"chat_copy": { "type": "string" }
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"HealthResponse": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"required": ["ok", "doctor", "status"],
|
|
32
|
+
"properties": {
|
|
33
|
+
"ok": { "type": "boolean" },
|
|
34
|
+
"doctor": { "type": "object" },
|
|
35
|
+
"status": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"properties": {
|
|
38
|
+
"skills_on_disk": { "type": "number" },
|
|
39
|
+
"core_skills": { "type": "number" },
|
|
40
|
+
"matrix_version": { "type": ["string", "null"] },
|
|
41
|
+
"capint_package": { "type": "string" }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"RecoverRequest": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"properties": {
|
|
49
|
+
"apply": { "type": "boolean", "default": false },
|
|
50
|
+
"latest": { "type": "boolean", "default": true }
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"FeedbackRequest": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"properties": {
|
|
56
|
+
"rating": { "type": "integer", "minimum": 1, "maximum": 5 },
|
|
57
|
+
"comment": { "type": "string", "maxLength": 2000 },
|
|
58
|
+
"source": { "type": "string", "default": "ui" }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"paths": {
|
|
63
|
+
"/api/health": {
|
|
64
|
+
"get": { "response": { "$ref": "#/definitions/HealthResponse" } }
|
|
65
|
+
},
|
|
66
|
+
"/api/route": {
|
|
67
|
+
"post": {
|
|
68
|
+
"request": { "$ref": "#/definitions/RouteRequest" },
|
|
69
|
+
"response": { "$ref": "#/definitions/RouteResponse" }
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"/api/recover": {
|
|
73
|
+
"post": {
|
|
74
|
+
"request": { "$ref": "#/definitions/RecoverRequest" },
|
|
75
|
+
"response": { "type": "object" }
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"/api/feedback": {
|
|
79
|
+
"post": {
|
|
80
|
+
"request": { "$ref": "#/definitions/FeedbackRequest" },
|
|
81
|
+
"response": { "type": "object", "properties": { "ok": { "type": "boolean" } } }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const root = path.join(__dirname, "..");
|
|
9
|
+
const schemaDir = path.join(root, "schemas");
|
|
10
|
+
const outDir = path.join(root, "docs", "generated");
|
|
11
|
+
|
|
12
|
+
const SCHEMAS = [
|
|
13
|
+
"execution-intent.v1.json",
|
|
14
|
+
"explanation.v2.json",
|
|
15
|
+
"ui-api.v1.json",
|
|
16
|
+
"explanation.v1.json"
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function propsTable(schema) {
|
|
20
|
+
const props = schema.properties || {};
|
|
21
|
+
const req = new Set(schema.required || []);
|
|
22
|
+
const lines = ["| Field | Type | Required |", "|-------|------|----------|"];
|
|
23
|
+
for (const [name, def] of Object.entries(props)) {
|
|
24
|
+
const type = def.enum ? def.enum.join(" \\| ") : def.type || def.$ref || "object";
|
|
25
|
+
lines.push(`| \`${name}\` | ${type} | ${req.has(name) ? "yes" : "no"} |`);
|
|
26
|
+
}
|
|
27
|
+
return lines.join("\n");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function renderSchemaDoc(fileName, schema) {
|
|
31
|
+
const title = schema.title || fileName;
|
|
32
|
+
return `# ${title}
|
|
33
|
+
|
|
34
|
+
> Auto-generated from \`schemas/${fileName}\`. Do not edit by hand.
|
|
35
|
+
|
|
36
|
+
**$id:** \`${schema.$id || ""}\`
|
|
37
|
+
|
|
38
|
+
## Properties
|
|
39
|
+
|
|
40
|
+
${propsTable(schema)}
|
|
41
|
+
|
|
42
|
+
## Raw schema
|
|
43
|
+
|
|
44
|
+
\`\`\`json
|
|
45
|
+
${JSON.stringify(schema, null, 2)}
|
|
46
|
+
\`\`\`
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function hashContent(content) {
|
|
51
|
+
return crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function generate() {
|
|
55
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
56
|
+
const written = [];
|
|
57
|
+
for (const file of SCHEMAS) {
|
|
58
|
+
const src = path.join(schemaDir, file);
|
|
59
|
+
if (!fs.existsSync(src)) continue;
|
|
60
|
+
const schema = JSON.parse(fs.readFileSync(src, "utf-8"));
|
|
61
|
+
const base = file.replace(/\.json$/, "");
|
|
62
|
+
const md = renderSchemaDoc(file, schema);
|
|
63
|
+
const dest = path.join(outDir, `${base}.md`);
|
|
64
|
+
fs.writeFileSync(dest, md, "utf-8");
|
|
65
|
+
written.push(dest);
|
|
66
|
+
}
|
|
67
|
+
const index = `# CapInt generated schema docs
|
|
68
|
+
|
|
69
|
+
${SCHEMAS.filter((f) => fs.existsSync(path.join(schemaDir, f)))
|
|
70
|
+
.map((f) => `- [${f.replace(/\.json$/, "")}](./${f.replace(/\.json$/, "")}.md)`)
|
|
71
|
+
.join("\n")}
|
|
72
|
+
`;
|
|
73
|
+
fs.writeFileSync(path.join(outDir, "README.md"), index, "utf-8");
|
|
74
|
+
return written;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function check() {
|
|
78
|
+
const tmp = [];
|
|
79
|
+
for (const file of SCHEMAS) {
|
|
80
|
+
const src = path.join(schemaDir, file);
|
|
81
|
+
if (!fs.existsSync(src)) continue;
|
|
82
|
+
const schema = JSON.parse(fs.readFileSync(src, "utf-8"));
|
|
83
|
+
const expected = renderSchemaDoc(file, schema);
|
|
84
|
+
const dest = path.join(outDir, `${file.replace(/\.json$/, "")}.md`);
|
|
85
|
+
if (!fs.existsSync(dest)) {
|
|
86
|
+
console.error(`missing generated doc: ${path.relative(root, dest)}`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
const actual = fs.readFileSync(dest, "utf-8");
|
|
90
|
+
if (hashContent(actual) !== hashContent(expected)) {
|
|
91
|
+
console.error(`stale generated doc: ${path.relative(root, dest)} — run: node scripts/generate-schema-docs.mjs`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
tmp.push(dest);
|
|
95
|
+
}
|
|
96
|
+
console.log(`schema docs check ok (${tmp.length} files)`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (process.argv.includes("--check")) {
|
|
100
|
+
check();
|
|
101
|
+
} else {
|
|
102
|
+
const files = generate();
|
|
103
|
+
console.log(`generated ${files.length} schema docs → docs/generated/`);
|
|
104
|
+
}
|
|
@@ -21,6 +21,12 @@ const steps = [
|
|
|
21
21
|
["upgrade", ["node", "tests/upgrade.test.mjs"]],
|
|
22
22
|
["local-adapters", ["node", "tests/local-adapters.test.mjs"]],
|
|
23
23
|
["explanation", ["node", "tests/explanation.test.mjs"]],
|
|
24
|
+
["providers-doctor", ["node", "tests/providers-doctor.test.mjs"]],
|
|
25
|
+
["stack", ["node", "tests/stack.test.mjs"]],
|
|
26
|
+
["skill-audit", ["node", "tests/skill-audit.test.mjs"]],
|
|
27
|
+
["cli-ui-parity", ["node", "tests/cli-ui-parity.test.mjs"]],
|
|
28
|
+
["generate-schema-docs", ["node", "scripts/generate-schema-docs.mjs"]],
|
|
29
|
+
["schema-docs-check", ["node", "scripts/generate-schema-docs.mjs", "--check"]],
|
|
24
30
|
["workspace-boundary", ["node", "tests/workspace-boundary.test.mjs"]],
|
|
25
31
|
["skill-disable", ["node", "tests/skill-disable.test.mjs"]],
|
|
26
32
|
["recover", ["node", "tests/recover.test.mjs"]],
|
|
@@ -33,6 +39,10 @@ const steps = [
|
|
|
33
39
|
["pipeline-smoke", ["node", "bin/capint.js", "pipeline", "i18n çeviri", "--force"]],
|
|
34
40
|
["doctor-smoke", ["node", "bin/capint.js", "doctor"]],
|
|
35
41
|
["audit-smoke", ["node", "bin/capint.js", "audit", "--json"]],
|
|
42
|
+
["skill-audit-smoke", ["node", "bin/capint.js", "skill", "audit", "--json"]],
|
|
43
|
+
["feedback-smoke", ["node", "bin/capint.js", "feedback", "--summary", "--json"]],
|
|
44
|
+
["providers-doctor-smoke", ["node", "bin/capint.js", "providers", "doctor", "--json"]],
|
|
45
|
+
["stack-doctor-smoke", ["node", "bin/capint.js", "stack", "doctor", "--json"]],
|
|
36
46
|
["consult-smoke", ["node", "bin/capint.js", "consult", "debug login", "--json"]],
|
|
37
47
|
["status-smoke", ["node", "bin/capint.js", "status", "--json"]]
|
|
38
48
|
];
|
|
@@ -37,12 +37,19 @@ You are the **invisible skill router**. Every task request passes through you fi
|
|
|
37
37
|
| `not_installed` | Do not pretend it exists. Use fallback or suggest `prismx skill add <name>`. |
|
|
38
38
|
| `project_added` | Treat as first-class for this project after reading its `SKILL.md`. |
|
|
39
39
|
|
|
40
|
-
##
|
|
40
|
+
## Context Check (Before Routing)
|
|
41
|
+
|
|
42
|
+
**CapInt project (root):**
|
|
43
|
+
1. Read `HANDOFF.md` if present → active task context
|
|
44
|
+
2. Skim `DONE.md` → don't rebuild completed work
|
|
45
|
+
3. Session rules: `AGENT.md` + gateway (this file)
|
|
46
|
+
|
|
47
|
+
**PrismX legacy (`.prismx/`):**
|
|
41
48
|
1. Read `.prismx/HANDOFF.md` → current task context
|
|
42
49
|
2. Read `.prismx/CURRENT_TASK.md` → today's focus (if present)
|
|
43
50
|
3. Check `.prismx/DONE.md` → don't rebuild completed features
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
|
|
52
|
+
Then: new session → prefer `session-context-primer` before heavy work; check complexity weight → 🟢 skip brainstorming, 🔴 always brainstorm first
|
|
46
53
|
|
|
47
54
|
## Quick Routing Table
|
|
48
55
|
| Keywords | Preferred Skill(s) | If Missing |
|
package/src/commands/doctor.js
CHANGED
|
@@ -29,6 +29,15 @@ module.exports = async function doctor(_args, flags) {
|
|
|
29
29
|
for (const i of report.issues) console.log(` x ${i}`);
|
|
30
30
|
console.log("");
|
|
31
31
|
}
|
|
32
|
+
if (report.actions?.length) {
|
|
33
|
+
console.log("Suggested actions:");
|
|
34
|
+
for (const a of report.actions) console.log(` → ${a.label} (${a.command})`);
|
|
35
|
+
console.log("");
|
|
36
|
+
}
|
|
37
|
+
if (report.summary_plain) {
|
|
38
|
+
console.log(`Summary: ${report.summary_plain}`);
|
|
39
|
+
console.log("");
|
|
40
|
+
}
|
|
32
41
|
console.log(report.hint);
|
|
33
42
|
console.log("");
|
|
34
43
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module.exports = async function feedback(args, flags) {
|
|
2
|
+
const rootDir = process.cwd();
|
|
3
|
+
const { summarizeFeedback } = require("../lib/feedback");
|
|
4
|
+
const sub = args[0];
|
|
5
|
+
|
|
6
|
+
if (sub === "summary" || flags.summary || (!sub && flags.json)) {
|
|
7
|
+
const summary = summarizeFeedback(rootDir);
|
|
8
|
+
if (flags.json) {
|
|
9
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
console.log("\n## CapInt feedback (local)\n");
|
|
13
|
+
console.log(`Entries: ${summary.count}`);
|
|
14
|
+
console.log(`Rated: ${summary.rated_count}`);
|
|
15
|
+
console.log(`Average: ${summary.average_rating ?? "—"}`);
|
|
16
|
+
if (Object.keys(summary.by_source).length) {
|
|
17
|
+
console.log("By source:");
|
|
18
|
+
for (const [k, v] of Object.entries(summary.by_source)) console.log(` ${k}: ${v}`);
|
|
19
|
+
}
|
|
20
|
+
console.log("");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
console.log(`
|
|
25
|
+
capint feedback — local wizard feedback (stored under .capint/feedback/)
|
|
26
|
+
|
|
27
|
+
capint feedback --summary [--json]
|
|
28
|
+
`);
|
|
29
|
+
};
|
package/src/commands/init.js
CHANGED
|
@@ -69,6 +69,18 @@ module.exports = async function init(args, flags) {
|
|
|
69
69
|
console.log("\nIDE sync skipped (--no-ide-sync). Run: capint ide sync");
|
|
70
70
|
}
|
|
71
71
|
console.log("\nNext: open GUNLUK.md — then chat your first task (no terminal needed)");
|
|
72
|
+
console.log(" capint stack setup — workflows + graphify + agentmemory checklist");
|
|
72
73
|
console.log(" capint route \"i18n\" (optional preview)");
|
|
74
|
+
console.log("Stack doc: docs/capint-stack.md");
|
|
73
75
|
console.log("Conflict policy: existing files without capint managed blocks are never overwritten.\n");
|
|
76
|
+
|
|
77
|
+
if (flags.stack) {
|
|
78
|
+
const { runStackSetup, formatStackSummary } = require("../lib/stack");
|
|
79
|
+
const stackResult = await runStackSetup(rootDir, { installGraphifyRule: !flags["no-graphify-install"] });
|
|
80
|
+
console.log("## Stack setup (post-init)\n");
|
|
81
|
+
for (const step of stackResult.steps) {
|
|
82
|
+
console.log(` [${step.status}] ${step.id}: ${step.action}`);
|
|
83
|
+
}
|
|
84
|
+
console.log(`\n${formatStackSummary(stackResult.doctor)}\n`);
|
|
85
|
+
}
|
|
74
86
|
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module.exports = async function providers(args, flags) {
|
|
2
|
+
const sub = args[0];
|
|
3
|
+
const rootDir = process.cwd();
|
|
4
|
+
const { runProvidersDoctor } = require("../lib/providers/doctor");
|
|
5
|
+
|
|
6
|
+
if (sub !== "doctor") {
|
|
7
|
+
console.log(`
|
|
8
|
+
capint providers — memory/graph sidecar diagnostics
|
|
9
|
+
|
|
10
|
+
capint providers doctor [--json]
|
|
11
|
+
|
|
12
|
+
Recipe: docs/capint-stack.md (detail: docs/recipes/memory-graph-pairing.md)
|
|
13
|
+
`);
|
|
14
|
+
if (!sub) return;
|
|
15
|
+
console.error(`Unknown subcommand: ${sub}`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const report = await runProvidersDoctor(rootDir);
|
|
20
|
+
|
|
21
|
+
if (flags.json) {
|
|
22
|
+
console.log(JSON.stringify(report, null, 2));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log("\n## CapInt providers doctor\n");
|
|
27
|
+
console.log(`Recipe: ${report.recipe}\n`);
|
|
28
|
+
|
|
29
|
+
console.log("Environment:");
|
|
30
|
+
console.log(` CAPINT_LOCAL_CONTEXT: ${report.env.CAPINT_LOCAL_CONTEXT || "(unset)"}`);
|
|
31
|
+
console.log(` CAPINT_MEMORY: ${report.env.CAPINT_MEMORY || "(unset)"}`);
|
|
32
|
+
console.log(` CAPINT_GRAPH: ${report.env.CAPINT_GRAPH || "(unset)"}`);
|
|
33
|
+
console.log(` local_context hot-path: ${report.env.local_context_enabled ? "on" : "off"}\n`);
|
|
34
|
+
|
|
35
|
+
for (const tier of Object.values(report.tiers)) {
|
|
36
|
+
const icon = tier.status === "ok" ? "✓" : tier.status === "partial" || tier.status === "cli_only" ? "~" : "✗";
|
|
37
|
+
console.log(`${icon} ${tier.label} — ${tier.status}`);
|
|
38
|
+
if (tier.id === "tier0_local") {
|
|
39
|
+
for (const p of tier.paths) console.log(` ${p.exists ? "✓" : "✗"} ${p.path}`);
|
|
40
|
+
}
|
|
41
|
+
if (tier.id === "tier1_graphify" && tier.artifacts_found?.length) {
|
|
42
|
+
for (const p of tier.artifacts_found) console.log(` ✓ ${p}`);
|
|
43
|
+
}
|
|
44
|
+
if (tier.id === "tier2_agentmemory" && tier.health?.error) {
|
|
45
|
+
console.log(` ${tier.health.error}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (report.recommendations.length) {
|
|
50
|
+
console.log("\nRecommendations:");
|
|
51
|
+
for (const r of report.recommendations) console.log(` - ${r}`);
|
|
52
|
+
}
|
|
53
|
+
console.log("");
|
|
54
|
+
};
|
package/src/commands/skill.js
CHANGED
|
@@ -18,6 +18,7 @@ capint skill — disable / enable / pin without deleting files
|
|
|
18
18
|
capint skill disable <name>
|
|
19
19
|
capint skill enable <name>
|
|
20
20
|
capint skill pin <name> promote to registry project_added tier
|
|
21
|
+
capint skill audit [--json] orphan / ghost / unreferenced skill report
|
|
21
22
|
|
|
22
23
|
Overlay: ${OVERLAY_PATH}
|
|
23
24
|
`);
|
|
@@ -85,6 +86,32 @@ Overlay: ${OVERLAY_PATH}
|
|
|
85
86
|
return;
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
if (sub === "audit") {
|
|
90
|
+
const { runSkillAudit } = require("../lib/skill-audit");
|
|
91
|
+
const report = runSkillAudit(rootDir);
|
|
92
|
+
if (flags.json) {
|
|
93
|
+
console.log(JSON.stringify(report, null, 2));
|
|
94
|
+
if (!report.pass) process.exit(report.exit_code);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
console.log("\n## Skill audit\n");
|
|
98
|
+
console.log(`Result: ${report.pass ? "PASS" : "FAIL"}`);
|
|
99
|
+
console.log(
|
|
100
|
+
`Findings: ${report.summary.issues} issues, ${report.summary.warnings} warnings, ${report.summary.info} info`
|
|
101
|
+
);
|
|
102
|
+
console.log(`Skills on disk: ${report.summary.skills_on_disk} · Registry: ${report.summary.registry_count}\n`);
|
|
103
|
+
for (const f of report.findings) {
|
|
104
|
+
console.log(` [${f.level}] ${f.message}`);
|
|
105
|
+
}
|
|
106
|
+
if (report.recommendations.length) {
|
|
107
|
+
console.log("\nRecommendations:");
|
|
108
|
+
for (const r of report.recommendations) console.log(` - ${r}`);
|
|
109
|
+
}
|
|
110
|
+
console.log("");
|
|
111
|
+
if (!report.pass) process.exit(report.exit_code);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
88
115
|
if (sub === "pin") {
|
|
89
116
|
if (!name) {
|
|
90
117
|
console.error("Usage: capint skill pin <name>");
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const {
|
|
2
|
+
runStackDoctor,
|
|
3
|
+
runStackSetup,
|
|
4
|
+
syncGraphArtifacts,
|
|
5
|
+
detectGraphPaths,
|
|
6
|
+
formatStackSummary,
|
|
7
|
+
STACK_DOC
|
|
8
|
+
} = require("../lib/stack");
|
|
9
|
+
|
|
10
|
+
module.exports = async function stack(args, flags) {
|
|
11
|
+
const sub = args[0];
|
|
12
|
+
const rootDir = process.cwd();
|
|
13
|
+
|
|
14
|
+
if (!sub || sub === "help" || flags.help) {
|
|
15
|
+
console.log(`
|
|
16
|
+
capint stack — unified CapInt + workflows + graphify + agentmemory
|
|
17
|
+
|
|
18
|
+
capint stack doctor [--json] 4-layer health (replaces mental model of providers only)
|
|
19
|
+
capint stack setup [--json] post-init wiring checklist + .capint/stack.json
|
|
20
|
+
capint stack graph paths suggest /graphify folders for this repo
|
|
21
|
+
capint stack graph sync copy graphify-out/ → CapInt paths
|
|
22
|
+
|
|
23
|
+
Doc: ${STACK_DOC}
|
|
24
|
+
`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (sub === "doctor") {
|
|
29
|
+
const report = await runStackDoctor(rootDir);
|
|
30
|
+
if (flags.json) {
|
|
31
|
+
console.log(JSON.stringify(report, null, 2));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
console.log("\n## CapInt stack doctor\n");
|
|
35
|
+
console.log(formatStackSummary(report));
|
|
36
|
+
console.log("");
|
|
37
|
+
if (report.graph_paths.suggested.length) {
|
|
38
|
+
console.log(`Suggested graphify: ${report.graph_paths.cursor_command}\n`);
|
|
39
|
+
}
|
|
40
|
+
if (report.recommendations.length) {
|
|
41
|
+
console.log("Next:");
|
|
42
|
+
for (const r of report.recommendations) console.log(` - ${r}`);
|
|
43
|
+
console.log("");
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (sub === "setup") {
|
|
49
|
+
const result = await runStackSetup(rootDir, { installGraphifyRule: !flags["no-graphify-install"] });
|
|
50
|
+
if (flags.json) {
|
|
51
|
+
console.log(JSON.stringify(result, null, 2));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
console.log("\n## CapInt stack setup\n");
|
|
55
|
+
console.log(`Wrote ${result.manifest ? ".capint/stack.json" : "manifest"}\n`);
|
|
56
|
+
for (const step of result.steps) {
|
|
57
|
+
console.log(` [${step.status}] ${step.id}: ${step.action}`);
|
|
58
|
+
if (step.then) console.log(` then: ${step.then}`);
|
|
59
|
+
if (step.detail && step.status !== "ok") console.log(` (${step.detail})`);
|
|
60
|
+
}
|
|
61
|
+
console.log(`\n${formatStackSummary(result.doctor)}\n`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (sub === "graph" && args[1] === "paths") {
|
|
66
|
+
const paths = detectGraphPaths(rootDir);
|
|
67
|
+
if (flags.json) {
|
|
68
|
+
console.log(JSON.stringify(paths, null, 2));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
console.log("\n## Graph paths for this project\n");
|
|
72
|
+
console.log(`Dirs found: ${paths.existing_dirs.join(", ") || "(none — will use .)"}`);
|
|
73
|
+
console.log(`Cursor: ${paths.cursor_command}`);
|
|
74
|
+
console.log(`Then: capint stack graph sync\n`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (sub === "graph" && args[1] === "sync") {
|
|
79
|
+
const result = syncGraphArtifacts(rootDir);
|
|
80
|
+
if (flags.json) {
|
|
81
|
+
console.log(JSON.stringify(result, null, 2));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!result.ok) {
|
|
85
|
+
console.error(result.error || "Nothing to sync");
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
console.log("\n## Graph synced to CapInt paths\n");
|
|
89
|
+
for (const a of result.actions) console.log(` ✓ ${a}`);
|
|
90
|
+
console.log("\nRun: capint stack doctor\n");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.error(`Unknown: capint stack ${args.join(" ")}`);
|
|
95
|
+
console.error("Try: capint stack help");
|
|
96
|
+
process.exit(1);
|
|
97
|
+
};
|
package/src/lib/audit.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require("fs");
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const { runDoctor } = require("./doctor");
|
|
4
4
|
const { loadMatrix } = require("./route-engine");
|
|
5
|
+
const { runSkillAudit } = require("./skill-audit");
|
|
5
6
|
|
|
6
7
|
const EXPECTED_AGENT_MARKERS = ["Execution Intent", "confirm", "parseIntent"];
|
|
7
8
|
|
|
@@ -89,6 +90,12 @@ function runAudit(rootDir) {
|
|
|
89
90
|
findings.push({ level: "warning", code: "doctor", message: w });
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
const skillAudit = runSkillAudit(rootDir);
|
|
94
|
+
for (const f of skillAudit.findings) {
|
|
95
|
+
findings.push(f);
|
|
96
|
+
}
|
|
97
|
+
recommendations.push(...skillAudit.recommendations);
|
|
98
|
+
|
|
92
99
|
const issueCount = findings.filter((f) => f.level === "issue").length;
|
|
93
100
|
const warningCount = findings.filter((f) => f.level === "warning").length;
|
|
94
101
|
|
|
@@ -100,7 +107,8 @@ function runAudit(rootDir) {
|
|
|
100
107
|
summary: { issues: issueCount, warnings: warningCount, info: findings.filter((f) => f.level === "info").length },
|
|
101
108
|
findings,
|
|
102
109
|
recommendations: [...new Set(recommendations)],
|
|
103
|
-
doctor_pass: doctor.pass
|
|
110
|
+
doctor_pass: doctor.pass,
|
|
111
|
+
skill_audit: skillAudit.summary
|
|
104
112
|
};
|
|
105
113
|
}
|
|
106
114
|
|
package/src/lib/contract.js
CHANGED
|
@@ -36,6 +36,15 @@ function validateExplanationShape(explanation, errors) {
|
|
|
36
36
|
if (typeof explanation.reason_summary !== "string") {
|
|
37
37
|
errors.push("explanation.reason_summary must be string");
|
|
38
38
|
}
|
|
39
|
+
if (explanation.reason_plain != null && typeof explanation.reason_plain !== "string") {
|
|
40
|
+
errors.push("explanation.reason_plain must be string when present");
|
|
41
|
+
}
|
|
42
|
+
if (explanation.tooltip != null && typeof explanation.tooltip !== "string") {
|
|
43
|
+
errors.push("explanation.tooltip must be string when present");
|
|
44
|
+
}
|
|
45
|
+
if (explanation.locale != null && !["tr", "en"].includes(explanation.locale)) {
|
|
46
|
+
errors.push("explanation.locale must be tr or en when present");
|
|
47
|
+
}
|
|
39
48
|
if (!Array.isArray(explanation.files_to_read)) {
|
|
40
49
|
errors.push("explanation.files_to_read must be array");
|
|
41
50
|
}
|
package/src/lib/doctor.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const { loadMatrix } = require("./route-engine");
|
|
4
|
-
const { validateExecutionIntent } = require("./contract");
|
|
5
4
|
const { loadDisabledSkills } = require("./disabled-skills");
|
|
5
|
+
const { listBackups } = require("./recover");
|
|
6
|
+
const { resolveLocale, t } = require("./i18n");
|
|
6
7
|
|
|
7
8
|
function loadRegistry(rootDir) {
|
|
8
9
|
const p = path.join(rootDir, "registry.json");
|
|
@@ -22,7 +23,60 @@ function skillsOnDisk(rootDir) {
|
|
|
22
23
|
.filter((n) => fs.existsSync(path.join(skillsDir, n, "SKILL.md")));
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
function
|
|
26
|
+
function buildDoctorActions({ issues, warnings, rootDir, locale }) {
|
|
27
|
+
const loc = resolveLocale(locale);
|
|
28
|
+
const actions = [];
|
|
29
|
+
const hasConfigIssue = issues.some(
|
|
30
|
+
(i) => i.includes("matrix") || i.includes("registry") || i.includes("parse error")
|
|
31
|
+
);
|
|
32
|
+
const { groups } = listBackups(rootDir);
|
|
33
|
+
|
|
34
|
+
if (hasConfigIssue && groups.length) {
|
|
35
|
+
actions.push({
|
|
36
|
+
id: "recover_latest",
|
|
37
|
+
safe: true,
|
|
38
|
+
command: "capint recover --latest --apply",
|
|
39
|
+
label_tr: t("ui.recover.apply", "tr"),
|
|
40
|
+
label_en: t("ui.recover.apply", "en"),
|
|
41
|
+
label: t("ui.recover.apply", loc)
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const w of warnings) {
|
|
46
|
+
const m = w.match(/^core skill disabled: (\S+)/);
|
|
47
|
+
if (m) {
|
|
48
|
+
actions.push({
|
|
49
|
+
id: "enable_core",
|
|
50
|
+
safe: true,
|
|
51
|
+
skill: m[1],
|
|
52
|
+
command: `capint skill enable ${m[1]}`,
|
|
53
|
+
label_tr: `Core skill'i aç: ${m[1]}`,
|
|
54
|
+
label_en: `Re-enable core skill: ${m[1]}`,
|
|
55
|
+
label: loc === "en" ? `Re-enable core skill: ${m[1]}` : `Core skill'i aç: ${m[1]}`
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return actions;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function buildSummaryPlain({ pass, issues, warnings, locale }) {
|
|
64
|
+
const loc = resolveLocale(locale);
|
|
65
|
+
if (pass && !warnings.length) return t("doctor.summary.ok", loc);
|
|
66
|
+
if (issues.some((i) => i.includes("matrix") || i.includes("registry"))) {
|
|
67
|
+
return t("doctor.summary.registry", loc);
|
|
68
|
+
}
|
|
69
|
+
if (warnings.some((w) => w.includes("disabled"))) {
|
|
70
|
+
return t("doctor.summary.disabled", loc);
|
|
71
|
+
}
|
|
72
|
+
if (!pass) {
|
|
73
|
+
return loc === "en" ? "Configuration issues found — see details below." : "Yapılandırma sorunları var — aşağıdaki detaylara bak.";
|
|
74
|
+
}
|
|
75
|
+
return loc === "en" ? "Healthy with minor warnings." : "Genel olarak sağlıklı; küçük uyarılar var.";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function runDoctor(rootDir, options = {}) {
|
|
79
|
+
const locale = resolveLocale(options.locale);
|
|
26
80
|
const issues = [];
|
|
27
81
|
const warnings = [];
|
|
28
82
|
const ok = [];
|
|
@@ -44,6 +98,11 @@ function runDoctor(rootDir) {
|
|
|
44
98
|
...(registry.optional || []),
|
|
45
99
|
...(registry.project_added || [])
|
|
46
100
|
]);
|
|
101
|
+
for (const name of disk) {
|
|
102
|
+
if (!allReg.has(name)) {
|
|
103
|
+
warnings.push(`Skill on disk but not in registry: ${name}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
47
106
|
for (const name of registry.core || []) {
|
|
48
107
|
if (!disk.has(name)) warnings.push(`core skill missing on disk: ${name}`);
|
|
49
108
|
}
|
|
@@ -100,6 +159,9 @@ function runDoctor(rootDir) {
|
|
|
100
159
|
hint = "Re-enable core skills: capint skill enable <name>";
|
|
101
160
|
}
|
|
102
161
|
|
|
162
|
+
const actions = buildDoctorActions({ issues, warnings, rootDir, locale });
|
|
163
|
+
const summary_plain = buildSummaryPlain({ pass, issues, warnings, locale });
|
|
164
|
+
|
|
103
165
|
return {
|
|
104
166
|
pass,
|
|
105
167
|
exit_code: pass ? 0 : 1,
|
|
@@ -107,7 +169,11 @@ function runDoctor(rootDir) {
|
|
|
107
169
|
warnings,
|
|
108
170
|
ok,
|
|
109
171
|
auto_fix: false,
|
|
110
|
-
hint
|
|
172
|
+
hint,
|
|
173
|
+
actions,
|
|
174
|
+
summary_plain,
|
|
175
|
+
summary_plain_tr: buildSummaryPlain({ pass, issues, warnings, locale: "tr" }),
|
|
176
|
+
summary_plain_en: buildSummaryPlain({ pass, issues, warnings, locale: "en" })
|
|
111
177
|
};
|
|
112
178
|
}
|
|
113
179
|
|
|
@@ -3,6 +3,7 @@ const path = require("path");
|
|
|
3
3
|
const { resolveOrchestration } = require("./orchestration");
|
|
4
4
|
const { disabledSet, overlayParseError } = require("./disabled-skills");
|
|
5
5
|
const { buildExplanation } = require("./explanation");
|
|
6
|
+
const { resolveLocale } = require("./i18n");
|
|
6
7
|
const { matchKeywords } = require("./intent-parser");
|
|
7
8
|
const { defaultConfirmOverride } = require("./env-flags");
|
|
8
9
|
const { detectWorkspaceBoundary } = require("./workspace-boundary");
|
|
@@ -157,7 +158,8 @@ function buildExecutionPolicy({ rawText, routeResult, memoryStrategy, matrix, pa
|
|
|
157
158
|
rootDir,
|
|
158
159
|
matchKeywords,
|
|
159
160
|
resolutionStatus: installStatus.resolution_status,
|
|
160
|
-
confirmDefaultOption: confirmBlock.default_option
|
|
161
|
+
confirmDefaultOption: confirmBlock.default_option,
|
|
162
|
+
locale: resolveLocale()
|
|
161
163
|
});
|
|
162
164
|
|
|
163
165
|
const out = {
|