Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9370a915c | |||
| 6d4a4819de | |||
| 2681a2d251 | |||
| c2eefe3578 | |||
| dd5e2e57dd |
@@ -10,7 +10,7 @@
|
|||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"name": "claude-mem",
|
"name": "claude-mem",
|
||||||
"version": "7.1.8",
|
"version": "7.1.9",
|
||||||
"source": "./plugin",
|
"source": "./plugin",
|
||||||
"description": "Persistent memory system for Claude Code - context compression across sessions"
|
"description": "Persistent memory system for Claude Code - context compression across sessions"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name: Feature request
|
name: Feature request
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: 'feature-request'
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
name: Convert Feature Requests to Discussions
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [labeled]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
issue_number:
|
||||||
|
description: 'Issue number to convert to discussion'
|
||||||
|
required: true
|
||||||
|
type: number
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
convert:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Only run on labeled event if the label is 'feature-request', or always run on workflow_dispatch
|
||||||
|
if: |
|
||||||
|
(github.event_name == 'issues' && github.event.label.name == 'feature-request') ||
|
||||||
|
github.event_name == 'workflow_dispatch'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
discussions: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Get issue details and create discussion
|
||||||
|
id: discussion
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
// Get issue details
|
||||||
|
let issue;
|
||||||
|
if (context.eventName === 'workflow_dispatch') {
|
||||||
|
const { data } = await github.rest.issues.get({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.payload.inputs.issue_number
|
||||||
|
});
|
||||||
|
issue = data;
|
||||||
|
} else {
|
||||||
|
issue = context.payload.issue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Processing issue #${issue.number}: ${issue.title}`);
|
||||||
|
|
||||||
|
// Format the discussion body with a reference to the original issue
|
||||||
|
const discussionBody = `> Originally posted as issue #${issue.number} by @${issue.user.login}\n> ${issue.html_url}\n\n${issue.body || 'No description provided.'}`;
|
||||||
|
|
||||||
|
const mutation = `
|
||||||
|
mutation($repositoryId: ID!, $categoryId: ID!, $title: String!, $body: String!) {
|
||||||
|
createDiscussion(input: {
|
||||||
|
repositoryId: $repositoryId
|
||||||
|
categoryId: $categoryId
|
||||||
|
title: $title
|
||||||
|
body: $body
|
||||||
|
}) {
|
||||||
|
discussion {
|
||||||
|
url
|
||||||
|
number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const variables = {
|
||||||
|
repositoryId: 'R_kgDOPng1Jw',
|
||||||
|
categoryId: 'DIC_kwDOPng1J84Cw86z',
|
||||||
|
title: issue.title,
|
||||||
|
body: discussionBody
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await github.graphql(mutation, variables);
|
||||||
|
const discussionUrl = result.createDiscussion.discussion.url;
|
||||||
|
const discussionNumber = result.createDiscussion.discussion.number;
|
||||||
|
|
||||||
|
core.setOutput('url', discussionUrl);
|
||||||
|
core.setOutput('number', discussionNumber);
|
||||||
|
core.setOutput('issue_number', issue.number);
|
||||||
|
|
||||||
|
console.log(`Created discussion #${discussionNumber}: ${discussionUrl}`);
|
||||||
|
return { discussionUrl, discussionNumber, issueNumber: issue.number };
|
||||||
|
} catch (error) {
|
||||||
|
core.setFailed(`Failed to create discussion: ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Comment on issue
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const issueNumber = ${{ steps.discussion.outputs.issue_number }};
|
||||||
|
const discussionUrl = '${{ steps.discussion.outputs.url }}';
|
||||||
|
|
||||||
|
const comment = `This feature request has been moved to [Discussions](${discussionUrl}) to keep bug reports separate from feature ideas.\n\nPlease continue the conversation there - we'd love to hear your thoughts!`;
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issueNumber,
|
||||||
|
body: comment
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Added comment to issue #${issueNumber}`);
|
||||||
|
|
||||||
|
- name: Close and lock issue
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const issueNumber = ${{ steps.discussion.outputs.issue_number }};
|
||||||
|
|
||||||
|
// Close the issue
|
||||||
|
await github.rest.issues.update({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issueNumber,
|
||||||
|
state: 'closed'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Closed issue #${issueNumber}`);
|
||||||
|
|
||||||
|
// Lock the issue
|
||||||
|
await github.rest.issues.lock({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: issueNumber,
|
||||||
|
lock_reason: 'resolved'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Locked issue #${issueNumber}`);
|
||||||
@@ -4,6 +4,40 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## [7.1.8] - 2025-12-13
|
||||||
|
|
||||||
|
## Memory Export/Import Scripts
|
||||||
|
|
||||||
|
Added portable memory export and import functionality with automatic duplicate prevention.
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
- **Export memories** to JSON format with search filtering and project-based filtering
|
||||||
|
- **Import memories** with automatic duplicate detection via composite keys
|
||||||
|
- Complete documentation in docs/public/usage/export-import.mdx
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
- Share memory sets between developers working on the same project
|
||||||
|
- Backup and restore specific project memories
|
||||||
|
- Collaborate on domain knowledge across teams
|
||||||
|
- Migrate memories between different claude-mem installations
|
||||||
|
|
||||||
|
### Example Usage
|
||||||
|
```bash
|
||||||
|
# Export Windows-related memories
|
||||||
|
npx tsx scripts/export-memories.ts "windows" windows-work.json
|
||||||
|
|
||||||
|
# Export only claude-mem project memories
|
||||||
|
npx tsx scripts/export-memories.ts "bugfix" fixes.json --project=claude-mem
|
||||||
|
|
||||||
|
# Import memories (with automatic duplicate prevention)
|
||||||
|
npx tsx scripts/import-memories.ts windows-work.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Technical Improvements
|
||||||
|
- Fixed JSON format response in /api/search endpoint for consistent structure
|
||||||
|
- Enhanced project filtering in ChromaDB hybrid search result hydration
|
||||||
|
- Duplicate detection using composite keys (session ID + title + timestamp)
|
||||||
|
|
||||||
## [7.1.7] - 2025-12-13
|
## [7.1.7] - 2025-12-13
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Claude-mem is a Claude Code plugin providing persistent memory across sessions. It captures tool usage, compresses observations using the Claude Agent SDK, and injects relevant context into future sessions.
|
Claude-mem is a Claude Code plugin providing persistent memory across sessions. It captures tool usage, compresses observations using the Claude Agent SDK, and injects relevant context into future sessions.
|
||||||
|
|
||||||
**Current Version**: 7.1.8
|
**Current Version**: 7.1.9
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claude-mem",
|
"name": "claude-mem",
|
||||||
"version": "7.1.8",
|
"version": "7.1.9",
|
||||||
"description": "Memory compression system for Claude Code - persist context across sessions",
|
"description": "Memory compression system for Claude Code - persist context across sessions",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"claude",
|
"claude",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claude-mem",
|
"name": "claude-mem",
|
||||||
"version": "7.1.8",
|
"version": "7.1.9",
|
||||||
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
|
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Alex Newman"
|
"name": "Alex Newman"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claude-mem-plugin",
|
"name": "claude-mem-plugin",
|
||||||
"version": "7.1.8",
|
"version": "7.1.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Runtime dependencies for claude-mem bundled hooks",
|
"description": "Runtime dependencies for claude-mem bundled hooks",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -73,6 +73,7 @@ interface StoredUserPrompt {
|
|||||||
|
|
||||||
export class ChromaSync {
|
export class ChromaSync {
|
||||||
private client: Client | null = null;
|
private client: Client | null = null;
|
||||||
|
private transport: StdioClientTransport | null = null;
|
||||||
private connected: boolean = false;
|
private connected: boolean = false;
|
||||||
private project: string;
|
private project: string;
|
||||||
private collectionName: string;
|
private collectionName: string;
|
||||||
@@ -101,7 +102,7 @@ export class ChromaSync {
|
|||||||
// See: https://github.com/thedotmack/claude-mem/issues/170 (Python 3.14 incompatibility)
|
// See: https://github.com/thedotmack/claude-mem/issues/170 (Python 3.14 incompatibility)
|
||||||
const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);
|
const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);
|
||||||
const pythonVersion = settings.CLAUDE_MEM_PYTHON_VERSION;
|
const pythonVersion = settings.CLAUDE_MEM_PYTHON_VERSION;
|
||||||
const transport = new StdioClientTransport({
|
this.transport = new StdioClientTransport({
|
||||||
command: 'uvx',
|
command: 'uvx',
|
||||||
args: [
|
args: [
|
||||||
'--python', pythonVersion,
|
'--python', pythonVersion,
|
||||||
@@ -119,7 +120,7 @@ export class ChromaSync {
|
|||||||
capabilities: {}
|
capabilities: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.client.connect(transport);
|
await this.client.connect(this.transport);
|
||||||
this.connected = true;
|
this.connected = true;
|
||||||
|
|
||||||
logger.info('CHROMA_SYNC', 'Connected to Chroma MCP server', { project: this.project });
|
logger.info('CHROMA_SYNC', 'Connected to Chroma MCP server', { project: this.project });
|
||||||
@@ -815,14 +816,38 @@ export class ChromaSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the Chroma client connection
|
* Close the Chroma client connection and cleanup subprocess
|
||||||
*/
|
*/
|
||||||
async close(): Promise<void> {
|
async close(): Promise<void> {
|
||||||
if (this.client && this.connected) {
|
if (!this.connected && !this.client && !this.transport) {
|
||||||
await this.client.close();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Close client first
|
||||||
|
if (this.client) {
|
||||||
|
try {
|
||||||
|
await this.client.close();
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn('CHROMA_SYNC', 'Error closing Chroma client', { project: this.project }, error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicitly close transport to kill subprocess
|
||||||
|
if (this.transport) {
|
||||||
|
try {
|
||||||
|
await this.transport.close();
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn('CHROMA_SYNC', 'Error closing transport', { project: this.project }, error as Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('CHROMA_SYNC', 'Chroma client and subprocess closed', { project: this.project });
|
||||||
|
} finally {
|
||||||
|
// Always reset state, even if errors occurred
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
this.client = null;
|
this.client = null;
|
||||||
logger.info('CHROMA_SYNC', 'Chroma client closed', { project: this.project });
|
this.transport = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user