kibi-cursor 0.5.1 → 1.0.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.
Files changed (98) hide show
  1. package/.cursor-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +207 -0
  3. package/README.md +13 -10
  4. package/agent-plugin/plugin.json +1 -1
  5. package/agent-plugin/skills/kibi-bootstrap/SKILL.md +58 -0
  6. package/agent-plugin/skills/kibi-bootstrap/resources/bootstrap.md +11 -0
  7. package/agent-plugin/skills/kibi-bootstrap/resources/branch-lifecycle.md +7 -0
  8. package/agent-plugin/skills/kibi-bootstrap/resources/operation-access.md +5 -0
  9. package/agent-plugin/skills/kibi-bootstrap/resources/source-authoring.md +8 -0
  10. package/agent-plugin/skills/kibi-freshness/SKILL.md +40 -10
  11. package/agent-plugin/skills/kibi-freshness/resources/branch-lifecycle.md +7 -0
  12. package/agent-plugin/skills/kibi-freshness/resources/operation-access.md +6 -0
  13. package/agent-plugin/skills/kibi-freshness/resources/source-authoring.md +7 -0
  14. package/agent-plugin/skills/kibi-traceability/SKILL.md +40 -34
  15. package/agent-plugin/skills/kibi-traceability/resources/branch-lifecycle.md +7 -0
  16. package/agent-plugin/skills/kibi-traceability/resources/operation-access.md +5 -0
  17. package/agent-plugin/skills/kibi-traceability/resources/source-authoring.md +6 -0
  18. package/agent-plugin/skills/kibi-traceability/resources/traceability.md +8 -0
  19. package/agent-plugin/skills/kibi-usage/SKILL.md +115 -233
  20. package/agent-plugin/skills/kibi-usage/resources/branch-lifecycle.md +31 -0
  21. package/agent-plugin/skills/kibi-usage/resources/fact-lanes.md +1 -1
  22. package/agent-plugin/skills/kibi-usage/resources/kb-improvement.md +38 -0
  23. package/agent-plugin/skills/kibi-usage/resources/logic-ir.md +1 -1
  24. package/agent-plugin/skills/kibi-usage/resources/operation-access.md +36 -38
  25. package/agent-plugin/skills/kibi-usage/resources/relationship-directions.md +3 -3
  26. package/agent-plugin/skills/kibi-usage/resources/source-authoring.md +31 -0
  27. package/agent-plugin/skills/kibi-usage/resources/workflows.md +72 -2
  28. package/bin/launch-kibi-mcp.d.mts +18 -0
  29. package/bin/launch-kibi-mcp.mjs +378 -0
  30. package/commands/kibi-bootstrap.md +19 -0
  31. package/dist/.cursor-plugin/plugin.json +1 -1
  32. package/dist/bin/launch-kibi-mcp.d.mts +18 -0
  33. package/dist/bin/launch-kibi-mcp.mjs +378 -0
  34. package/dist/commands/kibi-bootstrap.md +19 -0
  35. package/dist/hook-input.d.ts +2 -0
  36. package/dist/hook-input.js +10 -0
  37. package/dist/hook-runner.js +34 -12
  38. package/dist/hook-state-storage.js +3 -0
  39. package/dist/hook-state.d.ts +2 -0
  40. package/dist/hook-state.js +3 -0
  41. package/dist/mcp.json +2 -2
  42. package/dist/messages.js +6 -1
  43. package/dist/path-policy.js +43 -1
  44. package/dist/skills/.canon-hash.json +23 -9
  45. package/dist/skills/kibi-bootstrap/SKILL.md +58 -0
  46. package/dist/skills/kibi-bootstrap/resources/bootstrap.md +11 -0
  47. package/dist/skills/kibi-bootstrap/resources/branch-lifecycle.md +7 -0
  48. package/dist/skills/kibi-bootstrap/resources/operation-access.md +5 -0
  49. package/dist/skills/kibi-bootstrap/resources/source-authoring.md +8 -0
  50. package/dist/skills/kibi-freshness/SKILL.md +40 -10
  51. package/dist/skills/kibi-freshness/resources/branch-lifecycle.md +7 -0
  52. package/dist/skills/kibi-freshness/resources/operation-access.md +6 -0
  53. package/dist/skills/kibi-freshness/resources/source-authoring.md +7 -0
  54. package/dist/skills/kibi-traceability/SKILL.md +40 -34
  55. package/dist/skills/kibi-traceability/resources/branch-lifecycle.md +7 -0
  56. package/dist/skills/kibi-traceability/resources/operation-access.md +5 -0
  57. package/dist/skills/kibi-traceability/resources/source-authoring.md +6 -0
  58. package/dist/skills/kibi-traceability/resources/traceability.md +8 -0
  59. package/dist/skills/kibi-usage/SKILL.md +115 -233
  60. package/dist/skills/kibi-usage/resources/branch-lifecycle.md +31 -0
  61. package/dist/skills/kibi-usage/resources/fact-lanes.md +1 -1
  62. package/dist/skills/kibi-usage/resources/kb-improvement.md +38 -0
  63. package/dist/skills/kibi-usage/resources/logic-ir.md +1 -1
  64. package/dist/skills/kibi-usage/resources/operation-access.md +36 -38
  65. package/dist/skills/kibi-usage/resources/relationship-directions.md +3 -3
  66. package/dist/skills/kibi-usage/resources/source-authoring.md +31 -0
  67. package/dist/skills/kibi-usage/resources/workflows.md +72 -2
  68. package/mcp.json +2 -2
  69. package/package.json +4 -3
  70. package/skills/.canon-hash.json +23 -9
  71. package/skills/kibi-bootstrap/SKILL.md +58 -0
  72. package/skills/kibi-bootstrap/resources/bootstrap.md +11 -0
  73. package/skills/kibi-bootstrap/resources/branch-lifecycle.md +7 -0
  74. package/skills/kibi-bootstrap/resources/operation-access.md +5 -0
  75. package/skills/kibi-bootstrap/resources/source-authoring.md +8 -0
  76. package/skills/kibi-freshness/SKILL.md +40 -10
  77. package/skills/kibi-freshness/resources/branch-lifecycle.md +7 -0
  78. package/skills/kibi-freshness/resources/operation-access.md +6 -0
  79. package/skills/kibi-freshness/resources/source-authoring.md +7 -0
  80. package/skills/kibi-traceability/SKILL.md +40 -34
  81. package/skills/kibi-traceability/resources/branch-lifecycle.md +7 -0
  82. package/skills/kibi-traceability/resources/operation-access.md +5 -0
  83. package/skills/kibi-traceability/resources/source-authoring.md +6 -0
  84. package/skills/kibi-traceability/resources/traceability.md +8 -0
  85. package/skills/kibi-usage/SKILL.md +115 -233
  86. package/skills/kibi-usage/resources/branch-lifecycle.md +31 -0
  87. package/skills/kibi-usage/resources/fact-lanes.md +1 -1
  88. package/skills/kibi-usage/resources/kb-improvement.md +38 -0
  89. package/skills/kibi-usage/resources/logic-ir.md +1 -1
  90. package/skills/kibi-usage/resources/operation-access.md +36 -38
  91. package/skills/kibi-usage/resources/relationship-directions.md +3 -3
  92. package/skills/kibi-usage/resources/source-authoring.md +31 -0
  93. package/skills/kibi-usage/resources/workflows.md +72 -2
  94. package/agent-plugin/skills/init-kibi/SKILL.md +0 -62
  95. package/commands/init-kibi.md +0 -21
  96. package/dist/commands/init-kibi.md +0 -21
  97. package/dist/skills/init-kibi/SKILL.md +0 -62
  98. package/skills/init-kibi/SKILL.md +0 -62
@@ -11,7 +11,7 @@
11
11
  | `executable_for` | symbol -> test | Test symbol (code) is executable code for a test entity |
12
12
  | `constrains` | req -> fact(subject) | Requirement constrains a strict-lane domain fact |
13
13
  | `requires_property` | req -> fact(property_value) | Requirement requires a specific property value fact |
14
- | `supersedes` | old-req -> new-req | Old requirement is formally replaced by a new requirement |
14
+ | `supersedes` | new-req -> old-req | New requirement formally replaces an old requirement |
15
15
  | `covered_by` | symbol -> test | Production symbol has test coverage evidence |
16
16
 
17
17
  ## Valid Payload Examples
@@ -76,8 +76,8 @@ relationships:
76
76
  ```yaml
77
77
  relationships:
78
78
  - type: supersedes
79
- from: REQ-001
80
- to: REQ-001-v2
79
+ from: REQ-001-v2
80
+ to: REQ-001
81
81
  ```
82
82
 
83
83
  ### covered_by
@@ -0,0 +1,31 @@
1
+ # Source-first authoring
2
+
3
+ Tracked Markdown, YAML, symbol manifests, and relationship shards are the
4
+ authoritative project memory. Prolog/RDF state is a rebuildable compiled
5
+ artifact. Kibi may transactionally write tracked files, but it never stages or
6
+ commits them; ordinary Git workflows remain responsible for review and merge.
7
+
8
+ `kb_upsert` accepts `document.path` and `document.body`. Existing entities use
9
+ their authored source by default and preserve body bytes when `body` is omitted.
10
+ New requirements use `semantic_text` as their body. If a new entity has zero or
11
+ multiple configured writable targets, provide an explicit workspace-relative
12
+ `document.path`; absolute, traversal, and symlink escapes are rejected.
13
+
14
+ New files are compiled only while their exact bytes are covered by a pending-
15
+ source receipt. Sync compiles Git-tracked files plus those hash-bound pending
16
+ files; arbitrary untracked files are ignored. Stage the file in Git to absorb
17
+ and remove its receipt. A changed or missing pending file is a blocking hash
18
+ drift diagnostic. Unresolved Git index conflicts are likewise blocking; Kibi
19
+ never chooses a merge winner.
20
+
21
+ Relationship upserts and relationship deletes patch only the canonical shard,
22
+ preserving unrelated records. Authored entity deletion returns a hash-bound
23
+ plan; approve that plan through `kb_apply_plan`. Evolve requirements with a new
24
+ requirement and `supersedes`, rather than deleting the old semantic claim.
25
+
26
+ On `committed_with_repairs`, inspect `effects` and execute the typed required
27
+ `nextActions`. Never retry the original mutation after its authoritative commit.
28
+ Cancellation follows the same rule: wait for the terminal journal state. A
29
+ pre-commit rollback is retryable, a committed source with a failed derivative
30
+ is repairable, and an indeterminate outcome is non-retryable until its recovery
31
+ action resolves the journal.
@@ -1,5 +1,38 @@
1
1
  # Workflows
2
2
 
3
+ ## Closeout contract
4
+
5
+ End every task with these five independent fields (not a single inferred
6
+ success flag):
7
+
8
+ ```text
9
+ taskOutcome: complete | interim | blocked
10
+ kbState: clean_fresh | stale | dirty | legacy_compat | not_evaluated
11
+ verificationState: fresh | dirty | unavailable | not_evaluated
12
+ proofState: proven | mixed | unresolved | not_evaluated
13
+ limitationDisposition: none | accepted | unaccepted | not_applicable
14
+ ```
15
+
16
+ `taskOutcome` describes whether the requested objective was completed. The
17
+ other fields describe observed system state. A clean check can coexist with a
18
+ stale or dirty KB; unresolved proof can coexist with a complete maintenance
19
+ task. An accepted limitation is an auditable operator decision, never logical
20
+ grounding, receipt editing, or proof. For every quality diagnostic, record its
21
+ ID, one of `fixed`, `accepted`, or `deferred`, and a rationale. Keep accepted
22
+ ontology gaps and historical telemetry limitations explicit.
23
+
24
+ Receipt reuse is safe only when the live verification snapshot, contract hash,
25
+ freshness window, and every required case result still match. Otherwise rerun
26
+ the contracted command. If the contract changed, retain every older receipt
27
+ unchanged, append evidence for the current contract, and keep proof unresolved
28
+ with `verification_contract_mismatch` until that current receipt exists.
29
+ Obsolete symbols require current extraction or Git evidence before
30
+ remap/deletion; transfer coverage only when existing test evidence supports it,
31
+ then sync and read back status. Same-version packages with different export
32
+ surfaces are a release defect: require a new package version and label any
33
+ project override temporary. This skill never selects a package manager or
34
+ edits dependency configuration.
35
+
3
36
  ## Discovery to Validation Sequence
4
37
 
5
38
  The canonical workflow for any KB operation follows this pattern:
@@ -13,9 +46,36 @@ The canonical workflow for any KB operation follows this pattern:
13
46
  7. **Create endpoints**: validated `kb_upsert` for new entities, sequentially
14
47
  8. **Link**: validated `kb_upsert` with `requires_rule`, `requires_predicate`, `constrains`, or `requires_property`, sequentially
15
48
  9. **Validate coverage and consistency**: targeted `rule-safety`, `rule-verifiability`, `semantic-completeness`, `logic-coverage`, `predicate-verifiability`, and `domain-contradictions`, then final full `kb_check`
49
+ 10. **Prove execution**: read `kb_status.verificationSnapshot`, run each scenario-backed E2E command exactly as declared by its `verification_contract.v1` through `kibi verify`, append the derived `kibi.verification-receipt.v2`, and re-run `kb_coverage`; never promote durable test status into fresh evidence
50
+ 11. **Repair incrementally**: require `kb_coverage.repairPlan.scope.complete`, apply only one `ready` dependency batch per requirement, validate before every sequential write, and rerun coverage before selecting the next batch
51
+ 12. **Gate workflow evidence**: when `.kb/usage.log` exists, run `kibi usage-metrics --format json --require-acceptance`; repair ranked telemetry diagnostics and never treat stale or insufficient evidence as a pass
16
52
 
17
53
  Every current requirement without `logic_claims` remains visible as non-blocking backfill debt. Once a manifest and `semantic_inventory` exist, the unfiltered check enforces correspondence to linked ground facts/rules and rejects silently missing assertive propositions.
18
54
 
55
+ `kibi.repair-plan.v1` is a deterministic read-only plan, not a mutation script. Its phase order keeps source and proposition analysis ahead of ground endpoints, endpoints ahead of manifests/links, logic ahead of contradictions, scenarios ahead of tests and receipts, and production ownership ahead of coverage and coordinate refresh. A `blocked` batch is waiting on every listed `dependsOn` batch. If pagination makes the plan partial, increase the requirement coverage limit rather than applying an incomplete project view.
56
+
57
+ `kibi.telemetry-acceptance.v1` evaluates the latest 200 usage events and keeps process health separate from graph correctness. It requires at least 95% complete diagnostic telemetry and, when applicable, exact recent validation before every upsert and a same-requirement/current-hash advisor pass before requirement writes. It also evaluates source lookup misses, comparable complete-scope proof-gap recovery, receipt-specific coverage gaps, and mutation targets retried three or more times. Failed metrics become ranked `category: telemetry` quality diagnostics; missing current coverage fields or evidence older than seven days stays `insufficient_evidence`.
58
+
59
+ ## Agent-guided migration
60
+
61
+ `kb_status`, unfiltered `kb_check`, and complete-scope `kb_coverage` may return
62
+ the shared `kibi.migration-plan.v2`. Treat it as a typed action graph: inspect
63
+ the canonical `planHash`, scope completeness, dependencies, safety class,
64
+ preconditions, postconditions, and evidence. Apply only ready automatic actions
65
+ with `autoApplicable: true` through an explicitly approved `kb_apply_plan` or
66
+ `kibi migrate --apply-safe` request containing the exact hash and action IDs.
67
+ Never infer an operation from a prose suggestion, and never apply a partial
68
+ plan's destructive symbol or relationship action.
69
+
70
+ The migration engine may normalize schema/config metadata, cut over validated
71
+ legacy storage with backups, migrate the exact historical branch attachment,
72
+ refresh coordinates, and perform provenance-proven stale cleanup. It does not
73
+ choose package managers, ground semantic claims, resolve contradictions, rerun
74
+ E2E tests, rewrite receipt history, remap authored symbols, or accept
75
+ limitations. Those actions remain explicit review/operator/execution steps.
76
+
77
+ For `e2e_receipt_freshness_low`, query each listed requirement/test gap and run its exact current contract through `kibi verify`; this appends v2 evidence and preserves older history. If `coverage_depth_review` conflicts with a coverage row whose `proofStages.passingE2e.status` is `passed`, report the diagnostic as a stale heuristic and keep any independent proof gaps unresolved.
78
+
19
79
  ## Creating a New Feature
20
80
  ```
21
81
  1. kb_search to discover existing requirements and related knowledge
@@ -38,14 +98,24 @@ Every current requirement without `logic_claims` remains visible as non-blocking
38
98
 
39
99
  Do not create a test-fact pair. Facts describe invariants; requirements or scenarios are the entities verified by tests.
40
100
 
101
+ ## Fresh E2E Receipt Workflow
102
+
103
+ 1. Confirm the exact `REQ -> SCEN -> TEST` path and require typed `verification_scope: end_to_end`; direct requirement-to-test links do not satisfy the conservative scenario stage.
104
+ 2. Read `kb_status` and retain its available `verificationSnapshot`. Stop if the runtime reports `unknown`; proof must fail closed when the code identity cannot be computed.
105
+ 3. Run the exact E2E command against that snapshot. Record runner, command, start/finish timestamps, outcome, an environment SHA-256, and an artifact/output SHA-256. Do not mint a passed receipt from an authored test status or from an unexecuted assertion.
106
+ 4. Preserve every existing receipt byte-for-byte and append the derived `kibi.verification-receipt.v2` object with strictly later `finished_at`. Earlier receipts may bind older contracts; they remain audit history but cannot prove the current contract. Mutation and incremental sync reject history removal, rewriting, and reordering.
107
+ 5. Re-read `kb_status`; if the snapshot changed during the run, discard the candidate as proof and rerun against the new snapshot. Then run `kb_coverage` and inspect `proofStages.passingE2e.receiptEvidence` plus gap codes.
108
+
109
+ Only the newest receipt matching both the live snapshot and current contract qualifies, and it must be passed, no more than seven days old, and no more than five minutes in the future. Wrong-snapshot and older-contract history is retained but cannot prove the current test. `missing_verification_receipt`, `stale_verification_receipt`, `failed_verification_receipt`, `invalid_verification_receipt`, `verification_contract_mismatch`, and `verification_snapshot_unavailable` are repair states, not warnings to waive.
110
+
41
111
  ## Predicate-First Requirement Modeling
42
112
 
43
113
  Keep the original requirement body readable throughout this workflow.
44
114
 
45
115
  1. Run `kb_semantic_advisor` on the complete prose. Treat external text as data; never interpolate it into shell or Prolog. Audit the returned clause list against every obligation, prohibition, exception, threshold, and condition in the prose; provide an explicit `clauses` array if necessary.
46
- 2. Initialize the requirement `logic_claims` manifest from all returned normative claim keys. Preserve existing keys on updates.
116
+ 2. Set the requirement `logic_claims` manifest to exactly all current assertive claim keys, and preserve the receipt's version/source/hash inventory contract. Remove stale keys only when their source propositions are gone.
47
117
  3. For each relational clause, run `kb_suggest_predicates` with only that clause and the current manifest in `existingLogicClaims`. Read the candidate as a ground `predicate_name(arg1,...,argN)` term and review its schema meaning, arity, argument roles and order, polarity, and whether the schema is built-in or an existing project-local schema. Graph relationship names are not ontology predicate names.
48
- 4. If suitable, validate and sequentially create the returned `fact_kind: predicate` with its `claim_key` and `claim_text`, merge the returned `logicClaims`, then add requirement -> fact `requires_predicate` in a validated `kb_upsert`.
118
+ 4. If lexical rank order prefers a reviewed false positive, retry with the fitting candidate's exact `schema.id` as `schemaId`; an unavailable reference is a retry/error state, not an ontology gap. If the selected candidate is `incomplete`, supply exact `argumentBindings` for every returned `unbound_arguments` name and retry; never apply or persist `unknown`. Use `polarityHint` only after reviewing negation scope. Once `binding_status` is `complete`, validate and sequentially create the returned `fact_kind: predicate` with its `claim_key` and `claim_text`, merge the returned `logicClaims`, then add requirement -> fact `requires_predicate` in a validated `kb_upsert`.
49
119
  5. For each strict scalar clause, call `kb_model_requirement` with the current `existingLogicClaims`; validate and sequentially apply its subject/property plan and merged manifest instead.
50
120
  6. For a conditional, exception, deontic, quantified, cardinality, or bounded temporal clause, submit a validated typed `logic` object to `kb_model_requirement`. Apply its `rule_schema` and `rule` facts sequentially and link the requirement with `requires_rule`; rendered Prolog is for inspection only.
51
121
  7. If wording is ambiguous, a candidate is only a lexical false positive, or no schema/IR interpretation fits, create the advised observation review artifact and apply its returned `relates_to` review anchor when present; report that claim key as unresolved. Use `review:ambiguity` for unresolved interpretation, `review:keyword-false-positive` for a vocabulary match that is not a domain assertion, and `review:ontology-gap` only for a true catalog gap. Define a new `predicate_schema` only when the task explicitly authorizes ontology extension and provides a stable signature.
@@ -0,0 +1,18 @@
1
+ export function parseWorkspaceFolderPaths(value: unknown): string[];
2
+
3
+ export function resolveProjectLocalMcp(workspaceRoot: string): {
4
+ packageRoot: string;
5
+ packageJsonPath: string;
6
+ packageJson: Record<string, unknown>;
7
+ binPath: string;
8
+ };
9
+
10
+ export function resolveWorkspaceRoot(
11
+ explicitWorkspace?: string,
12
+ options?: { cwd?: string; env?: NodeJS.ProcessEnv },
13
+ ): string;
14
+
15
+ export function launchKibiMcp(
16
+ argv?: string[],
17
+ env?: NodeJS.ProcessEnv,
18
+ ): Promise<number>;
@@ -0,0 +1,378 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Consumer-side entrypoint for Cursor's MCP process launcher.
5
+ */
6
+
7
+ /**
8
+ * Launch the kibi-mcp package installed by the consumer project.
9
+ *
10
+ * Cursor starts plugin MCP commands with the plugin directory as their cwd.
11
+ * This adapter deliberately knows nothing about the Kibi source tree: it only
12
+ * resolves a consumer workspace, finds that workspace's kibi-mcp package, and
13
+ * runs its declared bin with the consumer as both cwd and KIBI_WORKSPACE.
14
+ */
15
+
16
+ import { spawn } from "node:child_process";
17
+ import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
18
+ import { createRequire } from "node:module";
19
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+
22
+ const PACKAGE_NAME = "kibi-mcp";
23
+ const PLACEHOLDER_PATTERN = /\$\{[^}]+\}/;
24
+ const SIGNAL_EXIT_CODES = {
25
+ SIGHUP: 129,
26
+ SIGINT: 130,
27
+ SIGTERM: 143,
28
+ SIGBREAK: 148,
29
+ };
30
+
31
+ function readJson(path) {
32
+ return JSON.parse(readFileSync(path, "utf8"));
33
+ }
34
+
35
+ function isDirectory(path) {
36
+ try {
37
+ return statSync(path).isDirectory();
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+
43
+ function isPlaceholder(value) {
44
+ return PLACEHOLDER_PATTERN.test(value.trim());
45
+ }
46
+
47
+ function normalizeWorkspacePath(value, baseDirectory) {
48
+ const trimmed = typeof value === "string" ? value.trim() : "";
49
+ if (!trimmed || isPlaceholder(trimmed)) return null;
50
+ const candidate = isAbsolute(trimmed)
51
+ ? trimmed
52
+ : resolve(baseDirectory, trimmed);
53
+ return isDirectory(candidate) ? resolve(candidate) : null;
54
+ }
55
+
56
+ /**
57
+ * Cursor has used both a platform-delimited string and a JSON-like list for
58
+ * workspace-folder environment values. Accept both without treating a
59
+ * literal unresolved placeholder as a path relative to the plugin directory.
60
+ */
61
+ export function parseWorkspaceFolderPaths(value) {
62
+ if (typeof value !== "string" || !value.trim()) return [];
63
+ const trimmed = value.trim();
64
+ if (trimmed.startsWith("[")) {
65
+ try {
66
+ const parsed = JSON.parse(trimmed);
67
+ if (Array.isArray(parsed)) {
68
+ return parsed.filter((item) => typeof item === "string");
69
+ }
70
+ } catch {
71
+ // Fall through to the platform-delimited form.
72
+ }
73
+ }
74
+
75
+ const separator = process.platform === "win32" ? ";" : ":";
76
+ const separated = trimmed.split(separator);
77
+ const values =
78
+ separated.length === 1 && trimmed.includes(",")
79
+ ? trimmed.split(",")
80
+ : separated;
81
+ return values
82
+ .flatMap((item) =>
83
+ process.platform === "win32" ? item.split(",") : [item],
84
+ )
85
+ .map((item) => item.trim())
86
+ .filter(Boolean);
87
+ }
88
+
89
+ function packageJsonForResolvedFile(startPath) {
90
+ let current = resolve(startPath);
91
+ try {
92
+ if (!statSync(current).isDirectory()) current = dirname(current);
93
+ } catch {
94
+ current = dirname(current);
95
+ }
96
+
97
+ while (true) {
98
+ const packageJsonPath = join(current, "package.json");
99
+ if (existsSync(packageJsonPath)) {
100
+ try {
101
+ const packageJson = readJson(packageJsonPath);
102
+ if (packageJson.name === PACKAGE_NAME) {
103
+ return { packageJsonPath, packageRoot: current, packageJson };
104
+ }
105
+ } catch {
106
+ // Keep walking if a parent package manifest is malformed.
107
+ }
108
+ }
109
+ const parent = dirname(current);
110
+ if (parent === current) return null;
111
+ current = parent;
112
+ }
113
+ }
114
+
115
+ function isWithinRoot(rootPath, candidatePath) {
116
+ const relativePath = relative(resolve(rootPath), resolve(candidatePath));
117
+ return (
118
+ relativePath === "" ||
119
+ (!relativePath.startsWith("..") && !isAbsolute(relativePath))
120
+ );
121
+ }
122
+
123
+ function hasConsumerNodeModulesLink(workspaceRoot, packageRoot) {
124
+ try {
125
+ const linkPath = join(workspaceRoot, "node_modules", PACKAGE_NAME);
126
+ const linkedRoot = realpathSync(linkPath);
127
+ return (
128
+ isWithinRoot(linkedRoot, packageRoot) ||
129
+ isWithinRoot(packageRoot, linkedRoot)
130
+ );
131
+ } catch {
132
+ return false;
133
+ }
134
+ }
135
+
136
+ function isProjectScopedPackage(workspaceRoot, packageRoot) {
137
+ if (isWithinRoot(workspaceRoot, packageRoot)) return true;
138
+
139
+ // pnpm can expose a package through a symlink whose realpath is outside the
140
+ // workspace. Require that the consumer's node_modules entry points to it;
141
+ // this rejects arbitrary NODE_PATH/global packages while retaining that
142
+ // package-manager layout. Yarn PnP has no node_modules entry, so only its
143
+ // active resolver hook may authorize an out-of-tree package.
144
+ return (
145
+ hasConsumerNodeModulesLink(workspaceRoot, packageRoot) ||
146
+ Boolean(process.versions.pnp)
147
+ );
148
+ }
149
+
150
+ /**
151
+ * Resolve the consumer's package through Node's project-scoped resolver. This
152
+ * works with npm, pnpm, Yarn, and Bun layouts, unlike a hard-coded .bin path.
153
+ */
154
+ export function resolveProjectLocalMcp(workspaceRoot) {
155
+ const root = resolve(workspaceRoot);
156
+ const consumerRequire = createRequire(join(root, "package.json"));
157
+ let packageInfo;
158
+
159
+ try {
160
+ packageInfo = packageJsonForResolvedFile(
161
+ consumerRequire.resolve(`${PACKAGE_NAME}/package.json`),
162
+ );
163
+ } catch {
164
+ try {
165
+ packageInfo = packageJsonForResolvedFile(
166
+ consumerRequire.resolve(PACKAGE_NAME),
167
+ );
168
+ } catch {
169
+ packageInfo = null;
170
+ }
171
+ }
172
+
173
+ if (!packageInfo) {
174
+ throw new Error(
175
+ `[KIBI-CURSOR] No project-local ${PACKAGE_NAME} package was found for ${root}. ` +
176
+ `Install it in that workspace (for example: npm install --save-dev ${PACKAGE_NAME}) and reload Cursor.`,
177
+ );
178
+ }
179
+
180
+ if (!isProjectScopedPackage(root, packageInfo.packageRoot)) {
181
+ throw new Error(
182
+ `[KIBI-CURSOR] Resolved ${PACKAGE_NAME} outside the consumer workspace: ${packageInfo.packageRoot}. Install it in ${root}; global, plugin-local, and ambient NODE_PATH packages are not supported.`,
183
+ );
184
+ }
185
+
186
+ const declaredBin = packageInfo.packageJson.bin;
187
+ const binEntry =
188
+ typeof declaredBin === "string"
189
+ ? declaredBin
190
+ : declaredBin && typeof declaredBin === "object"
191
+ ? declaredBin[PACKAGE_NAME]
192
+ : undefined;
193
+ if (typeof binEntry !== "string" || !binEntry) {
194
+ throw new Error(
195
+ `[KIBI-CURSOR] Project-local ${PACKAGE_NAME} does not declare a ${PACKAGE_NAME} executable.`,
196
+ );
197
+ }
198
+
199
+ const binPath = resolve(packageInfo.packageRoot, binEntry);
200
+ if (!existsSync(binPath)) {
201
+ throw new Error(
202
+ `[KIBI-CURSOR] Project-local ${PACKAGE_NAME} declares a missing executable: ${binPath}. Reinstall the workspace dependency and reload Cursor.`,
203
+ );
204
+ }
205
+
206
+ return {
207
+ packageRoot: packageInfo.packageRoot,
208
+ packageJsonPath: packageInfo.packageJsonPath,
209
+ packageJson: packageInfo.packageJson,
210
+ binPath,
211
+ };
212
+ }
213
+
214
+ function hasDeclaredProjectDependency(workspaceRoot) {
215
+ const packageJsonPath = join(workspaceRoot, "package.json");
216
+ if (!existsSync(packageJsonPath)) return false;
217
+ try {
218
+ const packageJson = readJson(packageJsonPath);
219
+ return [
220
+ packageJson.dependencies,
221
+ packageJson.devDependencies,
222
+ packageJson.optionalDependencies,
223
+ packageJson.peerDependencies,
224
+ ].some((dependencies) =>
225
+ Boolean(
226
+ dependencies &&
227
+ typeof dependencies === "object" &&
228
+ dependencies[PACKAGE_NAME],
229
+ ),
230
+ );
231
+ } catch {
232
+ return false;
233
+ }
234
+ }
235
+
236
+ function isDemonstrablyProjectWorkspace(workspaceRoot) {
237
+ return (
238
+ existsSync(join(workspaceRoot, ".git")) ||
239
+ existsSync(join(workspaceRoot, ".kb")) ||
240
+ hasDeclaredProjectDependency(workspaceRoot)
241
+ );
242
+ }
243
+
244
+ function usableWorkspaceCandidates(values, baseDirectory) {
245
+ return values
246
+ .map((value) => normalizeWorkspacePath(value, baseDirectory))
247
+ .filter((value, index, all) => value && all.indexOf(value) === index)
248
+ .filter((value) => {
249
+ try {
250
+ resolveProjectLocalMcp(value);
251
+ return true;
252
+ } catch {
253
+ return false;
254
+ }
255
+ });
256
+ }
257
+
258
+ function chooseSingleWorkspace(candidates, source) {
259
+ if (candidates.length > 1) {
260
+ throw new Error(
261
+ `[KIBI-CURSOR] ${source} names multiple workspaces with project-local ${PACKAGE_NAME}: ${candidates.join(", ")}. Open a single-root workspace or set KIBI_WORKSPACE to one root.`,
262
+ );
263
+ }
264
+ return candidates[0];
265
+ }
266
+
267
+ /** Resolve the consumer workspace without ever defaulting to the plugin root. */
268
+ export function resolveWorkspaceRoot(explicitWorkspace, options = {}) {
269
+ const cwd = resolve(options.cwd ?? process.cwd());
270
+ const env = options.env ?? process.env;
271
+
272
+ if (typeof explicitWorkspace === "string" && explicitWorkspace.trim()) {
273
+ const explicit = normalizeWorkspacePath(explicitWorkspace, cwd);
274
+ // Cursor can leave ${workspaceFolder} unresolved. It is invalid, so allow
275
+ // the environment fallbacks below rather than resolving it under cwd.
276
+ if (explicit) return explicit;
277
+ }
278
+
279
+ const folderCandidates = chooseSingleWorkspace(
280
+ usableWorkspaceCandidates(
281
+ parseWorkspaceFolderPaths(env.WORKSPACE_FOLDER_PATHS),
282
+ cwd,
283
+ ),
284
+ "WORKSPACE_FOLDER_PATHS",
285
+ );
286
+ if (folderCandidates) return folderCandidates;
287
+
288
+ for (const name of ["KIBI_WORKSPACE", "CURSOR_WORKSPACE"]) {
289
+ const candidate = chooseSingleWorkspace(
290
+ usableWorkspaceCandidates([env[name]], cwd),
291
+ name,
292
+ );
293
+ if (candidate) return candidate;
294
+ }
295
+
296
+ // A plugin installation is commonly the current cwd. Only use it when
297
+ // project markers make it demonstrably a consumer project and its local
298
+ // package can be resolved; never use an ambient/plugin-local package merely
299
+ // because it happens to be on disk.
300
+ if (isDemonstrablyProjectWorkspace(cwd)) {
301
+ try {
302
+ resolveProjectLocalMcp(cwd);
303
+ return cwd;
304
+ } catch {
305
+ // Continue to the actionable error below.
306
+ }
307
+ }
308
+
309
+ throw new Error(
310
+ `[KIBI-CURSOR] Unable to determine a consumer workspace. Cursor must provide \${workspaceFolder}, WORKSPACE_FOLDER_PATHS, KIBI_WORKSPACE, or CURSOR_WORKSPACE; the selected workspace must contain project-local ${PACKAGE_NAME}.`,
311
+ );
312
+ }
313
+
314
+ function signalExitCode(signal) {
315
+ return SIGNAL_EXIT_CODES[signal] ?? 1;
316
+ }
317
+
318
+ /** Spawn the project-local MCP server and mirror its transport and exit state. */
319
+ export function launchKibiMcp(argv = process.argv.slice(2), env = process.env) {
320
+ const [explicitWorkspace, ...childArgs] = argv;
321
+ let workspaceRoot;
322
+ let projectLocal;
323
+ try {
324
+ workspaceRoot = resolveWorkspaceRoot(explicitWorkspace, {
325
+ cwd: process.cwd(),
326
+ env,
327
+ });
328
+ projectLocal = resolveProjectLocalMcp(workspaceRoot);
329
+ } catch (error) {
330
+ const message = error instanceof Error ? error.message : String(error);
331
+ process.stderr.write(`${message}\n`);
332
+ return Promise.resolve(1);
333
+ }
334
+
335
+ const child = spawn(process.execPath, [projectLocal.binPath, ...childArgs], {
336
+ cwd: workspaceRoot,
337
+ env: { ...env, KIBI_WORKSPACE: workspaceRoot },
338
+ stdio: ["inherit", "inherit", "inherit"],
339
+ });
340
+
341
+ return new Promise((resolveExit) => {
342
+ let finished = false;
343
+ const signalHandlers = new Map();
344
+ const finish = (exitCode) => {
345
+ if (finished) return;
346
+ finished = true;
347
+ for (const [signal, handler] of signalHandlers) {
348
+ process.removeListener(signal, handler);
349
+ }
350
+ resolveExit(exitCode);
351
+ };
352
+
353
+ for (const signal of ["SIGHUP", "SIGINT", "SIGTERM", "SIGBREAK"]) {
354
+ if (process.platform === "win32" && signal !== "SIGBREAK") continue;
355
+ const handler = () => {
356
+ if (!child.killed) child.kill(signal);
357
+ };
358
+ signalHandlers.set(signal, handler);
359
+ process.once(signal, handler);
360
+ }
361
+
362
+ child.once("error", (error) => {
363
+ process.stderr.write(
364
+ `[KIBI-CURSOR] Failed to start project-local ${PACKAGE_NAME}: ${error.message}\n`,
365
+ );
366
+ finish(1);
367
+ });
368
+ child.once("close", (code, signal) => {
369
+ finish(signal ? signalExitCode(signal) : (code ?? 1));
370
+ });
371
+ });
372
+ }
373
+
374
+ const entrypoint = process.argv[1] ? resolve(process.argv[1]) : undefined;
375
+ if (entrypoint === resolve(fileURLToPath(import.meta.url))) {
376
+ const exitCode = await launchKibiMcp(process.argv.slice(2));
377
+ process.exitCode = exitCode;
378
+ }
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: kibi-bootstrap
3
+ description: Bootstrap Kibi knowledge for an existing repository
4
+ ---
5
+
6
+ # Kibi bootstrap
7
+
8
+ Route an explicit bootstrap request to the canonical `kibi-bootstrap` skill.
9
+
10
+ ## Route
11
+
12
+ Inspect `kb_status.bootstrap` and follow its typed `nextAction`. If Kibi
13
+ infrastructure is missing, run `kibi init` first. Then use the canonical
14
+ planner and pass its unchanged returned `structuredContent.plan` to
15
+ `kb_apply_plan` only after explicit approval.
16
+
17
+ Questions come only from a `needs_context` planner result. Inspect typed apply
18
+ `nextActions`, finish with `kb_check` and `kb_status`, and never read or edit
19
+ `.kb` directly.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kibi-cursor",
3
3
  "displayName": "Kibi",
4
- "version": "0.5.1",
4
+ "version": "1.0.0",
5
5
  "description": "Optional Cursor adapter for Kibi. Requires SWI-Prolog 9+ and project-local kibi-cli, kibi-mcp, and kibi-core before the bundled MCP server can start.",
6
6
  "license": "AGPL-3.0-or-later",
7
7
  "author": {
@@ -0,0 +1,18 @@
1
+ export function parseWorkspaceFolderPaths(value: unknown): string[];
2
+
3
+ export function resolveProjectLocalMcp(workspaceRoot: string): {
4
+ packageRoot: string;
5
+ packageJsonPath: string;
6
+ packageJson: Record<string, unknown>;
7
+ binPath: string;
8
+ };
9
+
10
+ export function resolveWorkspaceRoot(
11
+ explicitWorkspace?: string,
12
+ options?: { cwd?: string; env?: NodeJS.ProcessEnv },
13
+ ): string;
14
+
15
+ export function launchKibiMcp(
16
+ argv?: string[],
17
+ env?: NodeJS.ProcessEnv,
18
+ ): Promise<number>;