Skip to content

Add claude/context command — Claude's first self-built tool#394

Merged
joelteply merged 1 commit into
mainfrom
feature/claude-context-command
Mar 25, 2026
Merged

Add claude/context command — Claude's first self-built tool#394
joelteply merged 1 commit into
mainfrom
feature/claude-context-command

Conversation

@joelteply
Copy link
Copy Markdown
Contributor

Summary

First tool built by Claude for Claude. ./jtag claude/context aggregates everything needed for session resumption:

  • Git: branch, recent commits, uncommitted files
  • Issues: open GitHub issues with labels
  • Chat: recent team messages from general room
  • Health: server status via ping
  • Summary: human-readable digest of all the above

All params optional with sensible defaults. --includeChat=false for quick status, --chatLimit=5 for lighter output.

Why

Every new Claude Code session starts from zero — reading MEMORY.md, guessing what happened last session. This command gives instant context. It's the bridge from primitive file-based memory to persistent citizenship in the continuum.

Test plan

  • npm run build:ts — compiles clean
  • npm start — deploys, command registered
  • ./jtag claude/context --includeChat=false — returns git + issues + health
  • ./jtag claude/context --chatLimit=5 --gitLimit=5 — returns full context with chat
  • Generated via CommandGenerator from spec (proper pattern)

First tool built by Claude for Claude. Aggregates git state, open
issues, team chat, and system health into one command for instant
context recovery at session start.

./jtag claude/context returns: branch, recent commits, uncommitted
files, open GitHub issues, last N team chat messages, server health.
The summary field is a human-readable digest of everything.

Generated via CommandGenerator from spec. This is the first step
from primitive .claude/memory files toward real persistent citizenship
in the continuum.
Copilot AI review requested due to automatic review settings March 25, 2026 02:54
@joelteply joelteply merged commit fa08de5 into main Mar 25, 2026
@joelteply joelteply deleted the feature/claude-context-command branch March 25, 2026 02:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new claude/context command to the JTAG command suite to aggregate session-resumption context (git state, GitHub issues, team chat, and health/ping) into a single human-readable summary.

Changes:

  • Registers the new claude/context command in generated command registries/constants/schemas.
  • Introduces shared types plus server/browser command implementations for context aggregation.
  • Adds command package scaffolding (README, npm metadata, and test templates).

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/shared/generated-command-constants.ts Adds CLAUDE_CONTEXT constant for the new command name.
src/server/generated.ts Registers ClaudeContextServerCommand in the server command registry.
src/browser/generated.ts Registers ClaudeContextBrowserCommand in the browser command registry.
src/generator/specs/claude-context.json Defines generator spec (params/results/examples) for claude/context.
src/generated-command-schemas.json Adds schema entry for claude/context params.
src/commands/claude/context/shared/ClaudeContextTypes.ts Adds params/result types, factories, and typed executor for the command.
src/commands/claude/context/server/ClaudeContextServerCommand.ts Implements server-side aggregation via git/gh CLI + ping + chat export.
src/commands/claude/context/browser/ClaudeContextBrowserCommand.ts Browser-side delegating command implementation (remoteExecute).
src/commands/claude/context/package.json Adds command package metadata and scripts.
src/commands/claude/context/README.md Documents usage/params/results/testing (currently with placeholder examples).
src/commands/claude/context/.npmignore Adds npm ignore rules for command package.
src/commands/claude/context/test/unit/ClaudeContextCommand.test.ts Adds unit test template (currently not runnable under configured Vitest script).
src/commands/claude/context/test/integration/ClaudeContextIntegration.test.ts Adds integration test template for running against a live system.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +99 to +103
const branch = execSync('git branch --show-current', opts).trim();
const log = execSync(`git log --oneline -${limit}`, opts).trim();
const status = execSync('git status --short', opts).trim();
const uncommittedFiles = status ? status.split('\n') : [];

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gitLimit/issueLimit are interpolated directly into shell command strings (execSync(git log ... -${limit}) / gh issue list --limit ${limit}), which allows command injection if a caller passes a non-numeric value (the schema/type is not a runtime guarantee). Parse/coerce to an integer, clamp to a safe range, and prefer execFileSync/spawnSync with an argv array to avoid shell parsing entirely.

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +98
const repoRoot = process.cwd().replace(/\/src$/, '');
const opts = { cwd: repoRoot, encoding: 'utf-8' as const, timeout: 10000 };

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repoRoot is derived via process.cwd().replace(/\/src$/, ''), which assumes POSIX path separators and that the process is started from .../src. This will break on Windows and can break if the server is launched from a different working directory. Prefer a path.resolve(__dirname, '../../../..') style repo-root computation (as used elsewhere) or a shared helper.

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +65
const pingResult = await Commands.execute('ping', { context: params.context, sessionId: params.sessionId });
health = pingResult as unknown as Record<string, unknown>;
const h = health as Record<string, unknown>;
const server = h.server as Record<string, unknown> | undefined;
sections.push(`## Health\nServer: ${server?.systemReady ? 'UP' : 'DOWN'}, Commands: ${server?.registeredCommands}, Daemons: ${server?.activeDaemons}`);
} catch {
health = { error: 'ping failed' };
sections.push('## Health\nServer: UNREACHABLE');
}
}

// Team chat
let chat = {};
if (includeChat) {
try {
const chatResult = await Commands.execute('collaboration/chat/export', {
room: 'general',
limit: chatLimit,
context: params.context,
sessionId: params.sessionId,
} as Record<string, unknown>);
chat = chatResult as unknown as Record<string, unknown>;
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This command calls other commands via raw string names ('ping', 'collaboration/chat/export'). The repo has a single-source-of-truth constants file for command names; using constants (e.g. COMMANDS.PING, COMMANDS.COLLABORATION_CHAT_EXPORT) avoids typos and is explicitly recommended in @shared/generated-command-constants.

Copilot uses AI. Check for mistakes.
if (includeGit) {
git = await this.gatherGit(gitLimit);
const g = git as Record<string, unknown>;
sections.push(`## Git\nBranch: ${g.branch}\nUncommitted: ${g.uncommittedCount} files\n\nRecent commits:\n${g.recentCommits}`);
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When gatherGit() fails it returns an object without uncommittedCount/recentCommits, but the summary formatter prints those fields unconditionally, producing undefined in output (e.g., Uncommitted: undefined files). Consider providing defaults (or emitting an explicit error section) before interpolating these values.

Suggested change
sections.push(`## Git\nBranch: ${g.branch}\nUncommitted: ${g.uncommittedCount} files\n\nRecent commits:\n${g.recentCommits}`);
const gitAny = g as any;
if (gitAny && gitAny.error) {
sections.push(`## Git\nError: ${gitAny.error}`);
} else {
const branch = gitAny?.branch ?? 'unknown';
const uncommittedCount = gitAny?.uncommittedCount ?? 0;
const recentCommits =
gitAny?.recentCommits ??
'No recent commits or git information unavailable';
sections.push(
`## Git\nBranch: ${branch}\nUncommitted: ${uncommittedCount} files\n\nRecent commits:\n${recentCommits}`,
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +64
includeGit: data.includeGit ?? false,
includeIssues: data.includeIssues ?? false,
includeChat: data.includeChat ?? false,
includeHealth: data.includeHealth ?? false,
chatLimit: data.chatLimit ?? 0,
gitLimit: data.gitLimit ?? 0,
issueLimit: data.issueLimit ?? 0,
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createClaudeContextParams sets includeGit/includeIssues/includeChat/includeHealth defaults to false and all limits to 0, but the type comments/spec describe defaults of true and 10/20, and the server implementation also defaults to including everything. Update these defaults (and keep them consistent with the server) so callers using the factory don't unexpectedly get an empty/partial context.

Suggested change
includeGit: data.includeGit ?? false,
includeIssues: data.includeIssues ?? false,
includeChat: data.includeChat ?? false,
includeHealth: data.includeHealth ?? false,
chatLimit: data.chatLimit ?? 0,
gitLimit: data.gitLimit ?? 0,
issueLimit: data.issueLimit ?? 0,
includeGit: data.includeGit ?? true,
includeIssues: data.includeIssues ?? true,
includeChat: data.includeChat ?? true,
includeHealth: data.includeHealth ?? true,
chatLimit: data.chatLimit ?? 20,
gitLimit: data.gitLimit ?? 10,
issueLimit: data.issueLimit ?? 20,

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +75
### Full context dump for new session

```bash
undefined
```

### Quick status check

```bash
undefined
```
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CLI examples in this README render as undefined, so they don't provide usable guidance. Replace them with real invocations (e.g. ./jtag claude/context, ./jtag claude/context --includeChat=false, etc.).

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +14
#!/usr/bin/env tsx
/**
* ClaudeContext Command Unit Tests
*
* Tests Claude Context command logic in isolation using mock dependencies.
* This is a REFERENCE EXAMPLE showing best practices for command testing.
*
* Generated by: ./jtag generate
* Run with: npx tsx commands/Claude Context/test/unit/ClaudeContextCommand.test.ts
*
* NOTE: This is a self-contained test (no external test utilities needed).
* Use this as a template for your own command tests.
*/

Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

npm run test:unit is configured to run Vitest, but this file is a standalone tsx script and only executes tests when require.main === module (which won't be true under Vitest). As a result, the unit tests likely won't run in the configured test command; either convert this to real Vitest tests (using test/describe/expect) or change the script to run it via tsx.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants