Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions commands/gsd/knowledge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
name: gsd:knowledge
description: Initialize, index, and query the project knowledge layer — persistent codebase intelligence that survives context resets
argument-hint: "<subcommand> [args]"
allowed-tools:
- Read
- Write
- Edit
- Bash
- Glob
- Grep
---

<objective>
Manage the GSD Knowledge Layer — a persistent, structured intelligence layer that indexes your codebase, tracks patterns/decisions/conventions, and provides relevance-scored context to every executor agent.
</objective>

<subcommands>

## `init`
Initialize the knowledge layer for this project. Creates `.planning/knowledge/` with schema-compliant JSON files. Safe to run multiple times.

```bash
node get-shit-done/bin/gsd-tools.cjs knowledge init
```

## `index`
Full codebase index — scans all source files, computes SHA-256 hashes, extracts exports/imports, maps modules. Use `--incremental` for fast updates.

```bash
# Full rebuild
node get-shit-done/bin/gsd-tools.cjs knowledge index

# Incremental (only changed files since last index)
node get-shit-done/bin/gsd-tools.cjs knowledge index --incremental
```

## `deps`
Build the dependency graph — forward/reverse import maps, module-level dependencies, circular dependency detection.

```bash
node get-shit-done/bin/gsd-tools.cjs knowledge deps
```

## `context`
Assemble relevance-scored context for a task. Uses stemming, synonym expansion, confidence scoring, and token budgeting.

```bash
# Basic context assembly
node get-shit-done/bin/gsd-tools.cjs knowledge context "add authentication to the API"

# With diff awareness (boosts modules with recent changes)
node get-shit-done/bin/gsd-tools.cjs knowledge context "fix the login bug" --diff

# Per-agent budget
node get-shit-done/bin/gsd-tools.cjs knowledge context "write tests" --agent executor --budget 8000
```

## `modules`
List all indexed modules with file counts.

```bash
node get-shit-done/bin/gsd-tools.cjs knowledge modules
```

## `impact`
Show which files and modules are affected by changes to a specific file (reverse dependency lookup).

```bash
node get-shit-done/bin/gsd-tools.cjs knowledge impact src/auth/middleware.js
```

## `staleness`
Check if the knowledge index is stale and needs reindexing.

```bash
node get-shit-done/bin/gsd-tools.cjs knowledge staleness
```

</subcommands>

<process>

### First-time setup
1. Run `knowledge init` to create the directory structure
2. Run `knowledge index` to scan the codebase
3. Run `knowledge deps` to build the dependency graph

### Ongoing use
- Run `knowledge index --incremental` after making changes
- Run `knowledge context "<task>"` before starting work to get relevant context
- Run `knowledge staleness` to check if reindexing is needed

### How it integrates with GSD workflows
The knowledge layer is automatically queried during:
- **execute-phase**: Each executor agent receives relevance-scored context
- **plan-phase**: Planner gets module/dependency awareness
- **verify-work**: Verifier gets impact analysis for changed files

</process>
66 changes: 65 additions & 1 deletion get-shit-done/bin/gsd-tools.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ const frontmatter = require('./lib/frontmatter.cjs');
const profilePipeline = require('./lib/profile-pipeline.cjs');
const profileOutput = require('./lib/profile-output.cjs');
const workstream = require('./lib/workstream.cjs');
const knowledge = require('./lib/knowledge/index.cjs');

// ─── Arg parsing helpers ──────────────────────────────────────────────────────

Expand Down Expand Up @@ -274,7 +275,7 @@ async function main() {
const command = args[0];

if (!command) {
error('Usage: gsd-tools <command> [args] [--raw] [--pick <field>] [--cwd <path>] [--ws <name>]\nCommands: state, resolve-model, find-phase, commit, verify-summary, verify, frontmatter, template, generate-slug, current-timestamp, list-todos, verify-path-exists, config-ensure-section, config-new-project, init, workstream');
error('Usage: gsd-tools <command> [args] [--raw] [--pick <field>] [--cwd <path>] [--ws <name>]\nCommands: state, resolve-model, find-phase, commit, verify-summary, verify, frontmatter, template, generate-slug, current-timestamp, list-todos, verify-path-exists, config-ensure-section, config-new-project, init, workstream, knowledge');
}

// Multi-repo guard: resolve project root for commands that read/write .planning/.
Expand Down Expand Up @@ -910,6 +911,69 @@ async function runCommand(command, args, cwd, raw) {
break;
}

// ─── Knowledge Layer Commands ──────────────────────────────────────────────
case 'knowledge': {
const { output: coreOutput } = require('./lib/core.cjs');
const subcommand = args[1];

if (subcommand === 'init') {
const result = knowledge.initKnowledge(cwd);
coreOutput(result, raw);

} else if (subcommand === 'index') {
const incremental = args.includes('--incremental');
const result = incremental
? knowledge.updateIndexIncremental(cwd)
: knowledge.buildIndex(cwd);
coreOutput(result, raw);

} else if (subcommand === 'deps') {
const result = knowledge.buildDependencyGraph(cwd);
coreOutput(result, raw);

} else if (subcommand === 'context') {
const taskDesc = args.slice(2).filter(a => !a.startsWith('--')).join(' ');
if (!taskDesc) error('Usage: knowledge context <task description>');
const agentIdx = args.indexOf('--agent');
const agent = agentIdx !== -1 ? args[agentIdx + 1] : undefined;
const budgetIdx = args.indexOf('--budget');
const budget = budgetIdx !== -1 ? parseInt(args[budgetIdx + 1], 10) : undefined;
const diffAware = args.includes('--diff');
const result = diffAware
? knowledge.assembleDiffAwareContext(cwd, taskDesc, { agent, budget })
: knowledge.assembleContext(cwd, taskDesc, { agent, budget });
coreOutput(result, raw);

} else if (subcommand === 'modules') {
const result = knowledge.listModules(cwd);
coreOutput(result, raw);

} else if (subcommand === 'impact') {
const filePath = args[2];
if (!filePath) error('Usage: knowledge impact <file-path>');
const result = knowledge.getImpactedFiles(cwd, filePath);
coreOutput(result, raw);

} else if (subcommand === 'staleness') {
const ip = knowledge.indexPath(cwd);
if (!fs.existsSync(ip)) {
coreOutput({ stale: true, message: 'INDEX.json not found. Run: gsd-tools.cjs knowledge init && gsd-tools.cjs knowledge index' }, raw);
} else {
const index = knowledge.getIndex(cwd);
coreOutput({
stale: !index || index.last_mapped_commit === '0000000',
last_mapped_commit: index ? index.last_mapped_commit : null,
total_files: index ? (index.stats || {}).total_files : 0,
total_modules: index ? (index.stats || {}).total_modules : 0,
}, raw);
}

} else {
error('Unknown knowledge subcommand: ' + subcommand + '\nAvailable: init, index, deps, context, modules, impact, staleness');
}
break;
}

default:
error(`Unknown command: ${command}`);
}
Expand Down
67 changes: 67 additions & 0 deletions get-shit-done/bin/lib/knowledge/constants.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Knowledge Layer — Shared constants
* Ported from ACG's knowledge engine for GSD integration.
*/
'use strict';

/** Schema version for all knowledge JSON files. */
const VERSION = '1.0';

/** Directories to ignore during codebase walks. */
const IGNORE_DIRS = new Set([
'.git', 'node_modules', '.planning', 'dist', 'build', 'coverage',
'__pycache__', '.next', '.nuxt', '.svelte-kit', 'vendor', '.venv',
'venv', 'env', '.tox', '.mypy_cache', '.pytest_cache', '.turbo',
'.worktrees', 'gsd-local-patches',
]);

/** File extensions considered source code. */
const CODE_EXTS = new Set([
'.js', '.jsx', '.ts', '.tsx', '.py', '.rb', '.go', '.rs', '.java',
'.c', '.cpp', '.h', '.hpp', '.cs', '.php', '.swift', '.kt', '.scala',
'.sh', '.bash', '.zsh', '.sql', '.html', '.css', '.scss', '.json',
'.yaml', '.yml', '.toml', '.xml', '.md', '.vue', '.svelte', '.astro',
'.cjs', '.mjs',
]);

/** Extension to language name mapping. */
const LANG_MAP = {
'.js': 'javascript', '.jsx': 'javascript', '.cjs': 'javascript', '.mjs': 'javascript',
'.ts': 'typescript', '.tsx': 'typescript',
'.py': 'python', '.rb': 'ruby', '.go': 'go', '.rs': 'rust', '.java': 'java',
'.c': 'c', '.cpp': 'cpp', '.h': 'c', '.hpp': 'cpp', '.cs': 'csharp',
'.php': 'php', '.swift': 'swift', '.kt': 'kotlin', '.scala': 'scala',
'.sh': 'shell', '.bash': 'shell', '.zsh': 'shell',
'.sql': 'sql', '.html': 'html', '.css': 'css', '.scss': 'scss',
'.json': 'json', '.yaml': 'yaml', '.yml': 'yaml', '.toml': 'toml',
'.xml': 'xml', '.md': 'markdown', '.txt': 'text',
'.vue': 'vue', '.svelte': 'svelte', '.astro': 'astro',
};

/** Common English stop words removed from search queries. */
const STOP_WORDS = new Set([
'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
'should', 'may', 'might', 'shall', 'can', 'to', 'of', 'in', 'for',
'on', 'with', 'at', 'by', 'from', 'as', 'into', 'through', 'during',
'before', 'after', 'above', 'below', 'between', 'out', 'off', 'over',
'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when',
'where', 'why', 'how', 'all', 'each', 'every', 'both', 'few', 'more',
'most', 'other', 'some', 'such', 'only', 'own', 'same', 'so', 'than',
'too', 'very', 'just', 'because', 'and', 'or', 'but', 'not', 'no',
'nor', 'if', 'that', 'this', 'it', 'its', 'i', 'we', 'they', 'them',
'my', 'our', 'your', 'his', 'her', 'their', 'what', 'which', 'who',
'whom', 'these', 'those', 'am', 'about', 'up', 'also', 'need', 'make',
'like', 'use', 'using', 'used', 'get', 'got', 'new', 'want',
]);

/** File extensions considered binary (skip during indexing). */
const BINARY_EXTS = new Set([
'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.svg',
'.pdf', '.zip', '.tar', '.gz', '.tgz', '.bz2', '.7z', '.rar',
'.mp4', '.avi', '.mov', '.mkv', '.mp3', '.wav', '.flac',
'.woff', '.woff2', '.ttf', '.eot', '.otf',
'.exe', '.dll', '.so', '.dylib', '.bin',
]);

module.exports = { VERSION, IGNORE_DIRS, CODE_EXTS, LANG_MAP, STOP_WORDS, BINARY_EXTS };
Loading