@know-graph/cli 0.4.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/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/commands/coverage.d.ts +3 -0
- package/dist/commands/coverage.d.ts.map +1 -0
- package/dist/commands/coverage.js +98 -0
- package/dist/commands/coverage.js.map +1 -0
- package/dist/commands/export.d.ts +7 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +193 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/hook.d.ts +30 -0
- package/dist/commands/hook.d.ts.map +1 -0
- package/dist/commands/hook.js +224 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/index-cmd.d.ts +3 -0
- package/dist/commands/index-cmd.d.ts.map +1 -0
- package/dist/commands/index-cmd.js +96 -0
- package/dist/commands/index-cmd.js.map +1 -0
- package/dist/commands/index.d.ts +12 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +12 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +105 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/parse.d.ts +3 -0
- package/dist/commands/parse.d.ts.map +1 -0
- package/dist/commands/parse.js +130 -0
- package/dist/commands/parse.js.map +1 -0
- package/dist/commands/query.d.ts +3 -0
- package/dist/commands/query.d.ts.map +1 -0
- package/dist/commands/query.js +74 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/serve.d.ts +3 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +56 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/suggest.d.ts +3 -0
- package/dist/commands/suggest.d.ts.map +1 -0
- package/dist/commands/suggest.js +79 -0
- package/dist/commands/suggest.js.map +1 -0
- package/dist/commands/sync.d.ts +5 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +151 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/validate.d.ts +3 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +81 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/detect.d.ts +3 -0
- package/dist/utils/detect.d.ts.map +1 -0
- package/dist/utils/detect.js +153 -0
- package/dist/utils/detect.js.map +1 -0
- package/dist/utils/format.d.ts +16 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +37 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 KnowGraph Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @know-graph/cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for [KnowGraph](https://github.com/idosams/know-know) — make your codebase AI-navigable.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @know-graph/cli
|
|
9
|
+
# or
|
|
10
|
+
npx @know-graph/cli <command>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Commands
|
|
14
|
+
|
|
15
|
+
| Command | Description |
|
|
16
|
+
|---------|-------------|
|
|
17
|
+
| `knowgraph init` | Initialize KnowGraph in a project (creates `.knowgraph.yml`) |
|
|
18
|
+
| `knowgraph parse <path>` | Parse annotated files and output extracted entities |
|
|
19
|
+
| `knowgraph index [path]` | Build the SQLite index from annotated source files |
|
|
20
|
+
| `knowgraph query <term>` | Search the index with full-text search and filters |
|
|
21
|
+
| `knowgraph serve` | Start the MCP server for AI assistant integration |
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Initialize in your project
|
|
27
|
+
knowgraph init
|
|
28
|
+
|
|
29
|
+
# Add @knowgraph annotations to your code, then index
|
|
30
|
+
knowgraph index ./src
|
|
31
|
+
|
|
32
|
+
# Search your codebase
|
|
33
|
+
knowgraph query "authentication"
|
|
34
|
+
knowgraph query --owner "auth-team"
|
|
35
|
+
knowgraph query --tags "security,auth"
|
|
36
|
+
|
|
37
|
+
# Start MCP server for Claude integration
|
|
38
|
+
knowgraph serve
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Documentation
|
|
42
|
+
|
|
43
|
+
See the [main repository](https://github.com/idosams/know-know) for full documentation, annotation format, and examples.
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../src/commands/coverage.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqHzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkB9D"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @knowgraph
|
|
3
|
+
* type: module
|
|
4
|
+
* description: CLI command that reports documentation coverage for @knowgraph annotations
|
|
5
|
+
* owner: knowgraph-cli
|
|
6
|
+
* status: experimental
|
|
7
|
+
* tags: [cli, command, coverage, reporting]
|
|
8
|
+
* context:
|
|
9
|
+
* business_goal: Let developers measure and enforce annotation coverage in CI
|
|
10
|
+
* domain: cli
|
|
11
|
+
*/
|
|
12
|
+
import { resolve } from 'node:path';
|
|
13
|
+
import { statSync } from 'node:fs';
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import { calculateCoverage } from '@know-graph/core';
|
|
16
|
+
import { formatJson } from '../utils/format.js';
|
|
17
|
+
function formatPercentage(pct) {
|
|
18
|
+
if (pct >= 80)
|
|
19
|
+
return chalk.green(`${pct}%`);
|
|
20
|
+
if (pct >= 50)
|
|
21
|
+
return chalk.yellow(`${pct}%`);
|
|
22
|
+
return chalk.red(`${pct}%`);
|
|
23
|
+
}
|
|
24
|
+
function printBreakdownTable(title, breakdowns) {
|
|
25
|
+
console.log('');
|
|
26
|
+
console.log(chalk.bold(title));
|
|
27
|
+
console.log(` ${'Category'.padEnd(40)} ${'Annotated'.padStart(10)} ${'Total'.padStart(10)} ${'Coverage'.padStart(10)}`);
|
|
28
|
+
console.log(` ${'─'.repeat(70)}`);
|
|
29
|
+
for (const b of breakdowns) {
|
|
30
|
+
const category = b.category.length > 38 ? `${b.category.slice(0, 35)}...` : b.category;
|
|
31
|
+
console.log(` ${category.padEnd(40)} ${String(b.annotatedCount).padStart(10)} ${String(b.totalCount).padStart(10)} ${formatPercentage(b.percentage).padStart(19)}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function printTableOutput(result, byDimension) {
|
|
35
|
+
console.log('');
|
|
36
|
+
console.log(chalk.bold('Documentation Coverage Report'));
|
|
37
|
+
console.log(` Total files: ${chalk.cyan(String(result.totalFiles))}`);
|
|
38
|
+
console.log(` Annotated files: ${chalk.cyan(String(result.annotatedFiles))}`);
|
|
39
|
+
console.log(` Coverage: ${formatPercentage(result.percentage)}`);
|
|
40
|
+
if (!byDimension || byDimension === 'language') {
|
|
41
|
+
printBreakdownTable('By Language:', result.byLanguage);
|
|
42
|
+
}
|
|
43
|
+
if (!byDimension || byDimension === 'directory') {
|
|
44
|
+
printBreakdownTable('By Directory:', result.byDirectory);
|
|
45
|
+
}
|
|
46
|
+
if (!byDimension || byDimension === 'owner') {
|
|
47
|
+
printBreakdownTable('By Owner:', result.byOwner);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function runCoverage(targetPath, options) {
|
|
51
|
+
const absPath = resolve(targetPath);
|
|
52
|
+
try {
|
|
53
|
+
statSync(absPath);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
console.error(chalk.red(`Error: Path not found: ${absPath}`));
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const result = calculateCoverage({ rootDir: absPath });
|
|
62
|
+
if (options.format === 'json') {
|
|
63
|
+
console.log(formatJson(result, true));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
printTableOutput(result, options.by);
|
|
67
|
+
}
|
|
68
|
+
if (options.threshold !== undefined) {
|
|
69
|
+
const threshold = Number(options.threshold);
|
|
70
|
+
if (Number.isNaN(threshold)) {
|
|
71
|
+
console.error(chalk.red(`Error: Invalid threshold value: ${options.threshold}`));
|
|
72
|
+
process.exitCode = 1;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (result.percentage < threshold) {
|
|
76
|
+
console.error('');
|
|
77
|
+
console.error(chalk.red(`Coverage ${result.percentage}% is below threshold ${threshold}%`));
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export function registerCoverageCommand(program) {
|
|
88
|
+
program
|
|
89
|
+
.command('coverage [path]')
|
|
90
|
+
.description('Report documentation coverage for @knowgraph annotations')
|
|
91
|
+
.option('--format <format>', 'Output format (table|json)', 'table')
|
|
92
|
+
.option('--threshold <number>', 'Exit with code 1 if coverage is below this percentage')
|
|
93
|
+
.option('--by <dimension>', 'Breakdown dimension (language|directory|owner)')
|
|
94
|
+
.action((path, opts) => {
|
|
95
|
+
runCoverage(path ?? '.', opts);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=coverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/commands/coverage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { StoredEntity } from '@know-graph/core';
|
|
3
|
+
type ExportFormat = 'cursorrules' | 'markdown';
|
|
4
|
+
export declare function formatExport(entities: readonly StoredEntity[], format: ExportFormat): string;
|
|
5
|
+
export declare function registerExportCommand(program: Command): void;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,KAAK,YAAY,GAAG,aAAa,GAAG,UAAU,CAAC;AAuH/C,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,SAAS,YAAY,EAAE,EACjC,MAAM,EAAE,YAAY,GACnB,MAAM,CA8DR;AA2ED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAe5D"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @knowgraph
|
|
3
|
+
* type: module
|
|
4
|
+
* description: CLI command to export knowledge graph as .cursorrules or markdown
|
|
5
|
+
* owner: knowgraph-cli
|
|
6
|
+
* status: experimental
|
|
7
|
+
* tags: [cli, command, export, cursorrules]
|
|
8
|
+
* context:
|
|
9
|
+
* business_goal: Enable AI coding tools beyond MCP to leverage KnowGraph data
|
|
10
|
+
* domain: cli
|
|
11
|
+
*/
|
|
12
|
+
import { resolve } from 'node:path';
|
|
13
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import { createDatabaseManager, createQueryEngine, } from '@know-graph/core';
|
|
16
|
+
function getBusinessGoal(entity) {
|
|
17
|
+
const metadata = entity.metadata;
|
|
18
|
+
const context = metadata.context;
|
|
19
|
+
if (!context)
|
|
20
|
+
return undefined;
|
|
21
|
+
const goal = context.business_goal;
|
|
22
|
+
return typeof goal === 'string' ? goal : undefined;
|
|
23
|
+
}
|
|
24
|
+
function groupByOwner(entities) {
|
|
25
|
+
const ownerMap = new Map();
|
|
26
|
+
for (const entity of entities) {
|
|
27
|
+
const owner = entity.owner ?? '';
|
|
28
|
+
const existing = ownerMap.get(owner) ?? [];
|
|
29
|
+
ownerMap.set(owner, [...existing, entity]);
|
|
30
|
+
}
|
|
31
|
+
const groups = [];
|
|
32
|
+
for (const [owner, groupEntities] of ownerMap) {
|
|
33
|
+
if (owner !== '') {
|
|
34
|
+
groups.push({ owner, entities: groupEntities });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Sort owner groups alphabetically
|
|
38
|
+
groups.sort((a, b) => a.owner.localeCompare(b.owner));
|
|
39
|
+
// Add unowned group at the end if it exists
|
|
40
|
+
const unowned = ownerMap.get('');
|
|
41
|
+
if (unowned && unowned.length > 0) {
|
|
42
|
+
groups.push({ owner: '', entities: unowned });
|
|
43
|
+
}
|
|
44
|
+
return groups;
|
|
45
|
+
}
|
|
46
|
+
function groupByEntityType(entities) {
|
|
47
|
+
const typeMap = new Map();
|
|
48
|
+
for (const entity of entities) {
|
|
49
|
+
const existing = typeMap.get(entity.entityType) ?? [];
|
|
50
|
+
typeMap.set(entity.entityType, [...existing, entity]);
|
|
51
|
+
}
|
|
52
|
+
const groups = [];
|
|
53
|
+
for (const [entityType, groupEntities] of typeMap) {
|
|
54
|
+
groups.push({ entityType, entities: groupEntities });
|
|
55
|
+
}
|
|
56
|
+
return groups.sort((a, b) => a.entityType.localeCompare(b.entityType));
|
|
57
|
+
}
|
|
58
|
+
function formatEntityLine(entity) {
|
|
59
|
+
const location = `${entity.filePath}:${entity.line}`;
|
|
60
|
+
return `- **${entity.name}** (${entity.entityType}) - ${entity.description} [${location}]`;
|
|
61
|
+
}
|
|
62
|
+
function formatEntityDetails(entity) {
|
|
63
|
+
const lines = [];
|
|
64
|
+
const status = entity.status ?? 'unknown';
|
|
65
|
+
lines.push(`### ${entity.name} (${entity.entityType}) - ${status}`);
|
|
66
|
+
lines.push(`**File:** ${entity.filePath}:${entity.line}`);
|
|
67
|
+
lines.push(`**Owner:** ${entity.owner ?? 'unowned'}`);
|
|
68
|
+
lines.push(`**Description:** ${entity.description}`);
|
|
69
|
+
if (entity.tags.length > 0) {
|
|
70
|
+
lines.push(`**Tags:** ${entity.tags.join(', ')}`);
|
|
71
|
+
}
|
|
72
|
+
const businessGoal = getBusinessGoal(entity);
|
|
73
|
+
if (businessGoal) {
|
|
74
|
+
lines.push(`**Business Goal:** ${businessGoal}`);
|
|
75
|
+
}
|
|
76
|
+
return lines.join('\n');
|
|
77
|
+
}
|
|
78
|
+
function getFormatHeader(format) {
|
|
79
|
+
if (format === 'markdown') {
|
|
80
|
+
return [
|
|
81
|
+
'# Project Knowledge Graph',
|
|
82
|
+
'> Auto-generated by KnowGraph. Do not edit manually.',
|
|
83
|
+
'> Regenerate with: knowgraph export --format markdown',
|
|
84
|
+
'',
|
|
85
|
+
'> This file provides AI-readable context about the codebase.',
|
|
86
|
+
].join('\n');
|
|
87
|
+
}
|
|
88
|
+
return [
|
|
89
|
+
'# Project Knowledge Graph',
|
|
90
|
+
'> Auto-generated by KnowGraph. Do not edit manually.',
|
|
91
|
+
'> Regenerate with: knowgraph export --format cursorrules',
|
|
92
|
+
].join('\n');
|
|
93
|
+
}
|
|
94
|
+
export function formatExport(entities, format) {
|
|
95
|
+
const sections = [];
|
|
96
|
+
// Header
|
|
97
|
+
sections.push(getFormatHeader(format));
|
|
98
|
+
if (entities.length === 0) {
|
|
99
|
+
sections.push('## Architecture Overview');
|
|
100
|
+
sections.push('This project contains 0 annotated code entities. Run `knowgraph index .` to populate the knowledge graph.');
|
|
101
|
+
return sections.join('\n\n');
|
|
102
|
+
}
|
|
103
|
+
// Architecture Overview
|
|
104
|
+
const ownerGroups = groupByOwner(entities);
|
|
105
|
+
const namedOwners = ownerGroups.filter((g) => g.owner !== '');
|
|
106
|
+
const ownerCount = namedOwners.length;
|
|
107
|
+
sections.push('## Architecture Overview');
|
|
108
|
+
sections.push(`This project contains ${entities.length} annotated code entities across ${ownerCount} owners.`);
|
|
109
|
+
// Code Ownership section
|
|
110
|
+
sections.push('## Code Ownership');
|
|
111
|
+
for (const group of ownerGroups) {
|
|
112
|
+
if (group.owner === '')
|
|
113
|
+
continue;
|
|
114
|
+
const ownerLines = [];
|
|
115
|
+
ownerLines.push(`### ${group.owner}`);
|
|
116
|
+
const byType = groupByEntityType(group.entities);
|
|
117
|
+
for (const typeGroup of byType) {
|
|
118
|
+
for (const entity of typeGroup.entities) {
|
|
119
|
+
ownerLines.push(formatEntityLine(entity));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
sections.push(ownerLines.join('\n'));
|
|
123
|
+
}
|
|
124
|
+
// Unowned entities
|
|
125
|
+
const unownedGroup = ownerGroups.find((g) => g.owner === '');
|
|
126
|
+
if (unownedGroup) {
|
|
127
|
+
const unownedLines = [];
|
|
128
|
+
unownedLines.push('## Unowned Entities');
|
|
129
|
+
for (const entity of unownedGroup.entities) {
|
|
130
|
+
unownedLines.push(formatEntityLine(entity));
|
|
131
|
+
}
|
|
132
|
+
sections.push(unownedLines.join('\n'));
|
|
133
|
+
}
|
|
134
|
+
// Entity Details section
|
|
135
|
+
sections.push('## Entity Details');
|
|
136
|
+
for (const entity of entities) {
|
|
137
|
+
sections.push(formatEntityDetails(entity));
|
|
138
|
+
}
|
|
139
|
+
return sections.join('\n\n');
|
|
140
|
+
}
|
|
141
|
+
function getDefaultOutputFile(format) {
|
|
142
|
+
return format === 'cursorrules' ? '.cursorrules' : 'CODEBASE.md';
|
|
143
|
+
}
|
|
144
|
+
function runExport(targetPath, options) {
|
|
145
|
+
const absPath = resolve(targetPath);
|
|
146
|
+
const dbPath = resolve(absPath, '.knowgraph', 'knowgraph.db');
|
|
147
|
+
if (!existsSync(dbPath)) {
|
|
148
|
+
console.error(chalk.red(`Error: Database not found at ${dbPath}. Run 'knowgraph index ${targetPath}' first.`));
|
|
149
|
+
process.exitCode = 1;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const format = options.format;
|
|
153
|
+
if (format !== 'cursorrules' && format !== 'markdown') {
|
|
154
|
+
console.error(chalk.red(`Error: Invalid format '${format}'. Use 'cursorrules' or 'markdown'.`));
|
|
155
|
+
process.exitCode = 1;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
const dbManager = createDatabaseManager(dbPath);
|
|
160
|
+
try {
|
|
161
|
+
const queryEngine = createQueryEngine(dbManager);
|
|
162
|
+
const result = queryEngine.search({ query: '', limit: 10000 });
|
|
163
|
+
const entities = result.entities;
|
|
164
|
+
const content = formatExport(entities, format);
|
|
165
|
+
const outputFile = resolve(absPath, options.output ?? getDefaultOutputFile(format));
|
|
166
|
+
writeFileSync(outputFile, content, 'utf-8');
|
|
167
|
+
if (entities.length === 0) {
|
|
168
|
+
console.log(chalk.yellow(`Warning: No entities found in the index. Exported empty template to ${outputFile}`));
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.log(chalk.green(`Exported ${entities.length} entities to ${outputFile}`));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
dbManager.close();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
180
|
+
process.exitCode = 1;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export function registerExportCommand(program) {
|
|
184
|
+
program
|
|
185
|
+
.command('export [path]')
|
|
186
|
+
.description('Export knowledge graph as .cursorrules or markdown file')
|
|
187
|
+
.option('--format <format>', 'Output format (cursorrules|markdown)', 'cursorrules')
|
|
188
|
+
.option('--output <file>', 'Output file path')
|
|
189
|
+
.action((path, opts) => {
|
|
190
|
+
runExport(path ?? '.', opts);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
export declare const HOOK_MARKER_START = "# >>> KnowGraph pre-commit hook >>>";
|
|
3
|
+
export declare const HOOK_MARKER_END = "# <<< KnowGraph pre-commit hook <<<";
|
|
4
|
+
interface InstallOptions {
|
|
5
|
+
readonly force?: boolean;
|
|
6
|
+
}
|
|
7
|
+
interface InstallResult {
|
|
8
|
+
readonly success: boolean;
|
|
9
|
+
readonly appended?: boolean;
|
|
10
|
+
readonly alreadyInstalled?: boolean;
|
|
11
|
+
readonly error?: string;
|
|
12
|
+
}
|
|
13
|
+
interface UninstallResult {
|
|
14
|
+
readonly success: boolean;
|
|
15
|
+
readonly removed?: boolean;
|
|
16
|
+
readonly error?: string;
|
|
17
|
+
}
|
|
18
|
+
interface HookStatus {
|
|
19
|
+
readonly installed: boolean;
|
|
20
|
+
readonly hookPath?: string;
|
|
21
|
+
readonly error?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function getHookPath(): string | null;
|
|
24
|
+
export declare function buildHookScript(): string;
|
|
25
|
+
export declare function installHook(options?: InstallOptions): InstallResult;
|
|
26
|
+
export declare function uninstallHook(): UninstallResult;
|
|
27
|
+
export declare function getHookStatus(): HookStatus;
|
|
28
|
+
export declare function registerHookCommand(program: Command): void;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/commands/hook.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,eAAO,MAAM,iBAAiB,wCAAwC,CAAC;AACvE,eAAO,MAAM,eAAe,wCAAwC,CAAC;AAErE,UAAU,cAAc;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,UAAU,aAAa;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,eAAe;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,UAAU;IAClB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAS3C;AAED,wBAAgB,eAAe,IAAI,MAAM,CA+BxC;AA2CD,wBAAgB,WAAW,CAAC,OAAO,GAAE,cAAmB,GAAG,aAAa,CAsCvE;AAED,wBAAgB,aAAa,IAAI,eAAe,CA0B/C;AAED,wBAAgB,aAAa,IAAI,UAAU,CAmB1C;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgF1D"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @knowgraph
|
|
3
|
+
* type: module
|
|
4
|
+
* description: CLI command for managing git pre-commit hooks that validate @knowgraph annotations
|
|
5
|
+
* owner: knowgraph-cli
|
|
6
|
+
* status: stable
|
|
7
|
+
* tags: [cli, command, hook, pre-commit, git]
|
|
8
|
+
* context:
|
|
9
|
+
* business_goal: Automate annotation validation on every commit via git hooks
|
|
10
|
+
* domain: cli
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, readFileSync, writeFileSync, chmodSync, mkdirSync, unlinkSync, } from 'node:fs';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { execSync } from 'node:child_process';
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
export const HOOK_MARKER_START = '# >>> KnowGraph pre-commit hook >>>';
|
|
17
|
+
export const HOOK_MARKER_END = '# <<< KnowGraph pre-commit hook <<<';
|
|
18
|
+
export function getHookPath() {
|
|
19
|
+
try {
|
|
20
|
+
const gitRoot = execSync('git rev-parse --show-toplevel', {
|
|
21
|
+
encoding: 'utf-8',
|
|
22
|
+
}).trim();
|
|
23
|
+
return join(gitRoot, '.git', 'hooks', 'pre-commit');
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function buildHookScript() {
|
|
30
|
+
return [
|
|
31
|
+
'#!/bin/sh',
|
|
32
|
+
HOOK_MARKER_START,
|
|
33
|
+
'# Validates @knowgraph annotations in staged files',
|
|
34
|
+
'',
|
|
35
|
+
'STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E "\\.(ts|tsx|js|jsx|py)$")',
|
|
36
|
+
'',
|
|
37
|
+
'if [ -z "$STAGED_FILES" ]; then',
|
|
38
|
+
' exit 0',
|
|
39
|
+
'fi',
|
|
40
|
+
'',
|
|
41
|
+
'echo "KnowGraph: Validating annotations in staged files..."',
|
|
42
|
+
'',
|
|
43
|
+
'echo "$STAGED_FILES" | while IFS= read -r file; do',
|
|
44
|
+
' npx knowgraph parse "$file" --validate 2>&1',
|
|
45
|
+
' if [ $? -ne 0 ]; then',
|
|
46
|
+
' echo "KnowGraph: Validation failed for $file"',
|
|
47
|
+
' exit 1',
|
|
48
|
+
' fi',
|
|
49
|
+
'done',
|
|
50
|
+
'',
|
|
51
|
+
'# Propagate validation failure from subshell',
|
|
52
|
+
'if [ $? -ne 0 ]; then',
|
|
53
|
+
' exit 1',
|
|
54
|
+
'fi',
|
|
55
|
+
'',
|
|
56
|
+
'echo "KnowGraph: All annotations valid"',
|
|
57
|
+
HOOK_MARKER_END,
|
|
58
|
+
'',
|
|
59
|
+
].join('\n');
|
|
60
|
+
}
|
|
61
|
+
function getGitRoot() {
|
|
62
|
+
try {
|
|
63
|
+
return execSync('git rev-parse --show-toplevel', {
|
|
64
|
+
encoding: 'utf-8',
|
|
65
|
+
}).trim();
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function hasKnowgraphSection(content) {
|
|
72
|
+
return (content.includes(HOOK_MARKER_START) && content.includes(HOOK_MARKER_END));
|
|
73
|
+
}
|
|
74
|
+
function removeKnowgraphSection(content) {
|
|
75
|
+
const startIdx = content.indexOf(HOOK_MARKER_START);
|
|
76
|
+
const endIdx = content.indexOf(HOOK_MARKER_END);
|
|
77
|
+
if (startIdx === -1 || endIdx === -1) {
|
|
78
|
+
return content;
|
|
79
|
+
}
|
|
80
|
+
const before = content.slice(0, startIdx);
|
|
81
|
+
const after = content.slice(endIdx + HOOK_MARKER_END.length);
|
|
82
|
+
return (before + after).replace(/\n{3,}/g, '\n\n');
|
|
83
|
+
}
|
|
84
|
+
function extractKnowgraphSection() {
|
|
85
|
+
const full = buildHookScript();
|
|
86
|
+
const startIdx = full.indexOf(HOOK_MARKER_START);
|
|
87
|
+
const endIdx = full.indexOf(HOOK_MARKER_END) + HOOK_MARKER_END.length;
|
|
88
|
+
return full.slice(startIdx, endIdx);
|
|
89
|
+
}
|
|
90
|
+
function isOnlyKnowgraphContent(content) {
|
|
91
|
+
const stripped = removeKnowgraphSection(content)
|
|
92
|
+
.replace(/^#!\/bin\/sh\s*/, '')
|
|
93
|
+
.trim();
|
|
94
|
+
return stripped.length === 0;
|
|
95
|
+
}
|
|
96
|
+
export function installHook(options = {}) {
|
|
97
|
+
const { force = false } = options;
|
|
98
|
+
const gitRoot = getGitRoot();
|
|
99
|
+
if (gitRoot === null) {
|
|
100
|
+
return { success: false, error: 'Not in a git repository' };
|
|
101
|
+
}
|
|
102
|
+
const hooksDir = join(gitRoot, '.git', 'hooks');
|
|
103
|
+
const hookPath = join(hooksDir, 'pre-commit');
|
|
104
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
105
|
+
if (existsSync(hookPath)) {
|
|
106
|
+
const existingContent = readFileSync(hookPath, 'utf-8');
|
|
107
|
+
if (hasKnowgraphSection(existingContent)) {
|
|
108
|
+
if (!force) {
|
|
109
|
+
return { success: false, alreadyInstalled: true };
|
|
110
|
+
}
|
|
111
|
+
const updated = removeKnowgraphSection(existingContent);
|
|
112
|
+
const newContent = updated.trimEnd() + '\n\n' + extractKnowgraphSection() + '\n';
|
|
113
|
+
writeFileSync(hookPath, newContent);
|
|
114
|
+
chmodSync(hookPath, 0o755);
|
|
115
|
+
return { success: true, appended: false };
|
|
116
|
+
}
|
|
117
|
+
const newContent = existingContent.trimEnd() + '\n\n' + extractKnowgraphSection() + '\n';
|
|
118
|
+
writeFileSync(hookPath, newContent);
|
|
119
|
+
chmodSync(hookPath, 0o755);
|
|
120
|
+
return { success: true, appended: true };
|
|
121
|
+
}
|
|
122
|
+
writeFileSync(hookPath, buildHookScript());
|
|
123
|
+
chmodSync(hookPath, 0o755);
|
|
124
|
+
return { success: true };
|
|
125
|
+
}
|
|
126
|
+
export function uninstallHook() {
|
|
127
|
+
const gitRoot = getGitRoot();
|
|
128
|
+
if (gitRoot === null) {
|
|
129
|
+
return { success: false, error: 'Not in a git repository' };
|
|
130
|
+
}
|
|
131
|
+
const hookPath = join(gitRoot, '.git', 'hooks', 'pre-commit');
|
|
132
|
+
if (!existsSync(hookPath)) {
|
|
133
|
+
return { success: false, error: 'KnowGraph hook is not installed' };
|
|
134
|
+
}
|
|
135
|
+
const content = readFileSync(hookPath, 'utf-8');
|
|
136
|
+
if (!hasKnowgraphSection(content)) {
|
|
137
|
+
return { success: false, error: 'KnowGraph hook is not installed' };
|
|
138
|
+
}
|
|
139
|
+
if (isOnlyKnowgraphContent(content)) {
|
|
140
|
+
unlinkSync(hookPath);
|
|
141
|
+
return { success: true, removed: true };
|
|
142
|
+
}
|
|
143
|
+
const updated = removeKnowgraphSection(content);
|
|
144
|
+
writeFileSync(hookPath, updated);
|
|
145
|
+
return { success: true, removed: false };
|
|
146
|
+
}
|
|
147
|
+
export function getHookStatus() {
|
|
148
|
+
const gitRoot = getGitRoot();
|
|
149
|
+
if (gitRoot === null) {
|
|
150
|
+
return { installed: false, error: 'Not in a git repository' };
|
|
151
|
+
}
|
|
152
|
+
const hookPath = join(gitRoot, '.git', 'hooks', 'pre-commit');
|
|
153
|
+
if (!existsSync(hookPath)) {
|
|
154
|
+
return { installed: false, hookPath };
|
|
155
|
+
}
|
|
156
|
+
const content = readFileSync(hookPath, 'utf-8');
|
|
157
|
+
if (hasKnowgraphSection(content)) {
|
|
158
|
+
return { installed: true, hookPath };
|
|
159
|
+
}
|
|
160
|
+
return { installed: false, hookPath };
|
|
161
|
+
}
|
|
162
|
+
export function registerHookCommand(program) {
|
|
163
|
+
const hookCmd = program
|
|
164
|
+
.command('hook')
|
|
165
|
+
.description('Manage KnowGraph git pre-commit hooks');
|
|
166
|
+
hookCmd
|
|
167
|
+
.command('install')
|
|
168
|
+
.description('Install the KnowGraph pre-commit hook')
|
|
169
|
+
.option('--force', 'Overwrite existing KnowGraph hook section', false)
|
|
170
|
+
.action((options) => {
|
|
171
|
+
const result = installHook({ force: options.force });
|
|
172
|
+
if (result.alreadyInstalled) {
|
|
173
|
+
console.log(chalk.yellow('KnowGraph hook is already installed. Use --force to reinstall.'));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (!result.success) {
|
|
177
|
+
console.error(chalk.red(`Error: ${result.error}`));
|
|
178
|
+
process.exitCode = 1;
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (result.appended) {
|
|
182
|
+
console.log(chalk.green('KnowGraph hook appended to existing pre-commit hook.'));
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
console.log(chalk.green('KnowGraph pre-commit hook installed.'));
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
hookCmd
|
|
189
|
+
.command('uninstall')
|
|
190
|
+
.description('Remove the KnowGraph pre-commit hook')
|
|
191
|
+
.action(() => {
|
|
192
|
+
const result = uninstallHook();
|
|
193
|
+
if (!result.success) {
|
|
194
|
+
console.error(chalk.red(`Error: ${result.error}`));
|
|
195
|
+
process.exitCode = 1;
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (result.removed) {
|
|
199
|
+
console.log(chalk.green('KnowGraph pre-commit hook removed.'));
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
console.log(chalk.green('KnowGraph section removed from pre-commit hook (other hooks preserved).'));
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
hookCmd
|
|
206
|
+
.command('status')
|
|
207
|
+
.description('Check if the KnowGraph pre-commit hook is installed')
|
|
208
|
+
.action(() => {
|
|
209
|
+
const status = getHookStatus();
|
|
210
|
+
if (status.error) {
|
|
211
|
+
console.error(chalk.red(`Error: ${status.error}`));
|
|
212
|
+
process.exitCode = 1;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (status.installed) {
|
|
216
|
+
console.log(chalk.green(`KnowGraph hook is installed at ${status.hookPath}`));
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
console.log(chalk.yellow('KnowGraph hook is not installed.'));
|
|
220
|
+
console.log(chalk.dim('Run `knowgraph hook install` to install it.'));
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=hook.js.map
|