@shepai/cli 1.51.2 → 1.52.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/apis/json-schema/AgentFeature.yaml +1 -0
- package/apis/json-schema/AgentSession.yaml +38 -0
- package/apis/json-schema/AgentSessionMessage.yaml +27 -0
- package/dist/packages/core/src/application/ports/output/agents/agent-session-repository.interface.d.ts +59 -0
- package/dist/packages/core/src/application/ports/output/agents/agent-session-repository.interface.d.ts.map +1 -0
- package/dist/packages/core/src/application/ports/output/agents/agent-session-repository.interface.js +12 -0
- package/dist/packages/core/src/application/ports/output/agents/index.d.ts +1 -0
- package/dist/packages/core/src/application/ports/output/agents/index.d.ts.map +1 -1
- package/dist/packages/core/src/application/services/agents/agent-session-repository.registry.d.ts +24 -0
- package/dist/packages/core/src/application/services/agents/agent-session-repository.registry.d.ts.map +1 -0
- package/dist/packages/core/src/application/services/agents/agent-session-repository.registry.js +35 -0
- package/dist/packages/core/src/application/use-cases/agents/get-agent-session.use-case.d.ts +24 -0
- package/dist/packages/core/src/application/use-cases/agents/get-agent-session.use-case.d.ts.map +1 -0
- package/dist/packages/core/src/application/use-cases/agents/get-agent-session.use-case.js +48 -0
- package/dist/packages/core/src/application/use-cases/agents/list-agent-sessions.use-case.d.ts +22 -0
- package/dist/packages/core/src/application/use-cases/agents/list-agent-sessions.use-case.d.ts.map +1 -0
- package/dist/packages/core/src/application/use-cases/agents/list-agent-sessions.use-case.js +47 -0
- package/dist/packages/core/src/domain/errors/session-not-found.error.d.ts +10 -0
- package/dist/packages/core/src/domain/errors/session-not-found.error.d.ts.map +1 -0
- package/dist/packages/core/src/domain/errors/session-not-found.error.js +14 -0
- package/dist/packages/core/src/domain/generated/output.d.ts +56 -1
- package/dist/packages/core/src/domain/generated/output.d.ts.map +1 -1
- package/dist/packages/core/src/domain/generated/output.js +1 -0
- package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/container.js +20 -0
- package/dist/packages/core/src/infrastructure/services/agents/common/executors/claude-code-executor.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/common/executors/claude-code-executor.service.js +1 -0
- package/dist/packages/core/src/infrastructure/services/agents/sessions/claude-code-session.repository.d.ts +58 -0
- package/dist/packages/core/src/infrastructure/services/agents/sessions/claude-code-session.repository.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/agents/sessions/claude-code-session.repository.js +324 -0
- package/dist/packages/core/src/infrastructure/services/agents/sessions/stub-session.repository.d.ts +19 -0
- package/dist/packages/core/src/infrastructure/services/agents/sessions/stub-session.repository.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/agents/sessions/stub-session.repository.js +39 -0
- package/dist/src/presentation/cli/commands/session/index.d.ts +19 -0
- package/dist/src/presentation/cli/commands/session/index.d.ts.map +1 -0
- package/dist/src/presentation/cli/commands/session/index.js +25 -0
- package/dist/src/presentation/cli/commands/session/ls.command.d.ts +20 -0
- package/dist/src/presentation/cli/commands/session/ls.command.d.ts.map +1 -0
- package/dist/src/presentation/cli/commands/session/ls.command.js +186 -0
- package/dist/src/presentation/cli/commands/session/show.command.d.ts +14 -0
- package/dist/src/presentation/cli/commands/session/show.command.d.ts.map +1 -0
- package/dist/src/presentation/cli/commands/session/show.command.js +213 -0
- package/dist/src/presentation/cli/index.js +2 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/cache/.previewinfo +1 -1
- package/web/.next/cache/.rscinfo +1 -1
- package/web/.next/cache/.tsbuildinfo +1 -1
- package/web/.next/cache/config.json +3 -3
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +1 -1
- package/web/.next/required-server-files.json +1 -1
- package/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/page/server-reference-manifest.json +14 -14
- package/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +2 -2
- package/web/.next/server/chunks/ssr/_1fafa9db._.js +1 -1
- package/web/.next/server/chunks/ssr/_1fafa9db._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -1
- package/web/.next/server/pages/500.html +2 -2
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +15 -15
- package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +14 -14
- package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_1fafa9db._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +15 -15
- package/web/.next/standalone/src/presentation/web/server.js +1 -1
- package/web/.next/static/chunks/{f9953dad6d0c239c.js → 31e5030eed01a941.js} +1 -1
- package/web/.next/static/chunks/{f6f61237a0ec20e3.js → 45bb8ee2a6133d9d.js} +2 -2
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- /package/web/.next/static/{w8CFnR5hKioREFJyh8hpW → b2hejP8rfi4yw_GfgY-9J}/_buildManifest.js +0 -0
- /package/web/.next/static/{w8CFnR5hKioREFJyh8hpW → b2hejP8rfi4yw_GfgY-9J}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{w8CFnR5hKioREFJyh8hpW → b2hejP8rfi4yw_GfgY-9J}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code-executor.service.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/common/executors/claude-code-executor.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AACzF,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EAC1B,MAAM,4EAA4E,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"claude-code-executor.service.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/common/executors/claude-code-executor.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AACzF,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EAC1B,MAAM,4EAA4E,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAWjD;;;GAGG;AACH,qBAAa,yBAA0B,YAAW,cAAc;IAMlD,OAAO,CAAC,QAAQ,CAAC,KAAK;IALlC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAA8B;IAE3D,4EAA4E;IAC5E,OAAO,CAAC,MAAM,CAAS;gBAEM,KAAK,EAAE,aAAa;IAEjD,4EAA4E;IAC5E,OAAO,CAAC,GAAG;IAML,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAmHtF,aAAa,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,aAAa,CAAC,yBAAyB,CAAC;IA+F3C;;;OAGG;IACH,OAAO,CAAC,cAAc;IAmCtB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAI/C,OAAO,CAAC,SAAS;IAajB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,eAAe;CAkExB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Session Repository
|
|
3
|
+
*
|
|
4
|
+
* Infrastructure implementation of IAgentSessionRepository for Claude Code.
|
|
5
|
+
* Reads JSONL session files from ~/.claude/projects/ using a lazy stat-then-parse
|
|
6
|
+
* strategy for performance: stat all files in parallel for mtime-based sorting,
|
|
7
|
+
* then fully parse only the top-N files needed for the list view.
|
|
8
|
+
*
|
|
9
|
+
* File structure:
|
|
10
|
+
* ~/.claude/projects/<encoded-project-path>/<uuid>.jsonl
|
|
11
|
+
*
|
|
12
|
+
* Subagent sessions stored in subdirectories are excluded by only reading
|
|
13
|
+
* depth-1 .jsonl files from each project directory.
|
|
14
|
+
*/
|
|
15
|
+
import type { AgentSession } from '../../../../domain/generated/output.js';
|
|
16
|
+
import type { IAgentSessionRepository, ListSessionsOptions, GetSessionOptions } from '../../../../application/ports/output/agents/agent-session-repository.interface.js';
|
|
17
|
+
/** Extra metadata extracted from session JSONL that isn't in the domain type */
|
|
18
|
+
export interface SessionMetadata {
|
|
19
|
+
cliVersion?: string;
|
|
20
|
+
gitBranch?: string;
|
|
21
|
+
permissionMode?: string;
|
|
22
|
+
userType?: string;
|
|
23
|
+
toolUsage: Record<string, number>;
|
|
24
|
+
userMessageCount: number;
|
|
25
|
+
assistantMessageCount: number;
|
|
26
|
+
}
|
|
27
|
+
export declare class ClaudeCodeSessionRepository implements IAgentSessionRepository {
|
|
28
|
+
private readonly basePath;
|
|
29
|
+
constructor(basePath?: string);
|
|
30
|
+
isSupported(): boolean;
|
|
31
|
+
list(options?: ListSessionsOptions): Promise<AgentSession[]>;
|
|
32
|
+
findById(id: string, options?: GetSessionOptions): Promise<AgentSession | null>;
|
|
33
|
+
/** Collect all depth-1 .jsonl session files with mtime from all project directories */
|
|
34
|
+
private collectSessionFiles;
|
|
35
|
+
/** Collect depth-1 .jsonl files from a single project directory with stat for mtime */
|
|
36
|
+
private collectDepthOneJsonlFiles;
|
|
37
|
+
/**
|
|
38
|
+
* Find a session file by exact or prefix ID match, scanning all project directories.
|
|
39
|
+
* Supports prefix matching so users can pass truncated IDs (e.g. first 8 chars).
|
|
40
|
+
* Returns the match info or null if not found / ambiguous.
|
|
41
|
+
*/
|
|
42
|
+
private findSessionFile;
|
|
43
|
+
/**
|
|
44
|
+
* Parse a JSONL session file into an AgentSession.
|
|
45
|
+
*
|
|
46
|
+
* Throws on any JSON parse failure so the caller can skip the file.
|
|
47
|
+
*/
|
|
48
|
+
private parseSessionFile;
|
|
49
|
+
/**
|
|
50
|
+
* Extract plain text from message content.
|
|
51
|
+
* - string content: returned as-is
|
|
52
|
+
* - array content: concatenates all text blocks; falls back to tool_use summary
|
|
53
|
+
*/
|
|
54
|
+
private extractTextContent;
|
|
55
|
+
/** Replace home directory prefix with ~ in a file path */
|
|
56
|
+
private abbreviatePath;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=claude-code-session.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-session.repository.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/sessions/claude-code-session.repository.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,OAAO,KAAK,EACV,YAAY,EAGb,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EACV,uBAAuB,EACvB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,mFAAmF,CAAC;AA6B3F,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,qBACa,2BAA4B,YAAW,uBAAuB;IAC7D,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,GAAE,MAAuD;IAE9F,WAAW,IAAI,OAAO;IAIhB,IAAI,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAuB5D,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAmBrF,uFAAuF;YACzE,mBAAmB;IAyBjC,uFAAuF;YACzE,yBAAyB;IAsBvC;;;;OAIG;YACW,eAAe;IAkD7B;;;;OAIG;YACW,gBAAgB;IA6H9B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IA8B1B,0DAA0D;IAC1D,OAAO,CAAC,cAAc;CAQvB"}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Session Repository
|
|
3
|
+
*
|
|
4
|
+
* Infrastructure implementation of IAgentSessionRepository for Claude Code.
|
|
5
|
+
* Reads JSONL session files from ~/.claude/projects/ using a lazy stat-then-parse
|
|
6
|
+
* strategy for performance: stat all files in parallel for mtime-based sorting,
|
|
7
|
+
* then fully parse only the top-N files needed for the list view.
|
|
8
|
+
*
|
|
9
|
+
* File structure:
|
|
10
|
+
* ~/.claude/projects/<encoded-project-path>/<uuid>.jsonl
|
|
11
|
+
*
|
|
12
|
+
* Subagent sessions stored in subdirectories are excluded by only reading
|
|
13
|
+
* depth-1 .jsonl files from each project directory.
|
|
14
|
+
*/
|
|
15
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
16
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
17
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
18
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
19
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
20
|
+
};
|
|
21
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
22
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
23
|
+
};
|
|
24
|
+
import * as fs from 'node:fs/promises';
|
|
25
|
+
import * as os from 'node:os';
|
|
26
|
+
import * as path from 'node:path';
|
|
27
|
+
import { injectable } from 'tsyringe';
|
|
28
|
+
let ClaudeCodeSessionRepository = class ClaudeCodeSessionRepository {
|
|
29
|
+
basePath;
|
|
30
|
+
constructor(basePath = path.join(os.homedir(), '.claude', 'projects')) {
|
|
31
|
+
this.basePath = basePath;
|
|
32
|
+
}
|
|
33
|
+
isSupported() {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
async list(options) {
|
|
37
|
+
const limit = options?.limit ?? 20;
|
|
38
|
+
const fileInfos = await this.collectSessionFiles();
|
|
39
|
+
fileInfos.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
40
|
+
const topFiles = limit === 0 ? fileInfos : fileInfos.slice(0, limit);
|
|
41
|
+
const sessions = [];
|
|
42
|
+
for (const fileInfo of topFiles) {
|
|
43
|
+
try {
|
|
44
|
+
const session = await this.parseSessionFile(fileInfo, { includeMessages: false });
|
|
45
|
+
if (session !== null) {
|
|
46
|
+
sessions.push(session);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Malformed or unreadable file — skip silently
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return sessions;
|
|
54
|
+
}
|
|
55
|
+
async findById(id, options) {
|
|
56
|
+
const messageLimit = options?.messageLimit ?? 20;
|
|
57
|
+
const match = await this.findSessionFile(id);
|
|
58
|
+
if (match === null)
|
|
59
|
+
return null;
|
|
60
|
+
try {
|
|
61
|
+
const stat = await fs.stat(match.filePath);
|
|
62
|
+
const fileInfo = {
|
|
63
|
+
id: match.resolvedId,
|
|
64
|
+
filePath: match.filePath,
|
|
65
|
+
mtime: stat.mtime,
|
|
66
|
+
};
|
|
67
|
+
return await this.parseSessionFile(fileInfo, { includeMessages: true, messageLimit });
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Collect all depth-1 .jsonl session files with mtime from all project directories */
|
|
74
|
+
async collectSessionFiles() {
|
|
75
|
+
let entries;
|
|
76
|
+
try {
|
|
77
|
+
entries = await fs.readdir(this.basePath, { withFileTypes: true, encoding: 'utf-8' });
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
const projectDirs = entries
|
|
83
|
+
.filter((e) => e.isDirectory())
|
|
84
|
+
.map((e) => path.join(this.basePath, e.name));
|
|
85
|
+
const results = await Promise.allSettled(projectDirs.map((dir) => this.collectDepthOneJsonlFiles(dir)));
|
|
86
|
+
const fileInfos = [];
|
|
87
|
+
for (const result of results) {
|
|
88
|
+
if (result.status === 'fulfilled') {
|
|
89
|
+
fileInfos.push(...result.value);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return fileInfos;
|
|
93
|
+
}
|
|
94
|
+
/** Collect depth-1 .jsonl files from a single project directory with stat for mtime */
|
|
95
|
+
async collectDepthOneJsonlFiles(projectDir) {
|
|
96
|
+
const entries = await fs.readdir(projectDir, { withFileTypes: true, encoding: 'utf-8' });
|
|
97
|
+
const jsonlFiles = entries.filter((e) => e.isFile() && e.name.endsWith('.jsonl'));
|
|
98
|
+
const statResults = await Promise.allSettled(jsonlFiles.map(async (e) => {
|
|
99
|
+
const filePath = path.join(projectDir, e.name);
|
|
100
|
+
const stat = await fs.stat(filePath);
|
|
101
|
+
const id = e.name.slice(0, -'.jsonl'.length);
|
|
102
|
+
return { id, filePath, mtime: stat.mtime };
|
|
103
|
+
}));
|
|
104
|
+
const fileInfos = [];
|
|
105
|
+
for (const result of statResults) {
|
|
106
|
+
if (result.status === 'fulfilled') {
|
|
107
|
+
fileInfos.push(result.value);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return fileInfos;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Find a session file by exact or prefix ID match, scanning all project directories.
|
|
114
|
+
* Supports prefix matching so users can pass truncated IDs (e.g. first 8 chars).
|
|
115
|
+
* Returns the match info or null if not found / ambiguous.
|
|
116
|
+
*/
|
|
117
|
+
async findSessionFile(id) {
|
|
118
|
+
let entries;
|
|
119
|
+
try {
|
|
120
|
+
entries = await fs.readdir(this.basePath, { withFileTypes: true, encoding: 'utf-8' });
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const projectDirs = entries
|
|
126
|
+
.filter((e) => e.isDirectory())
|
|
127
|
+
.map((e) => path.join(this.basePath, e.name));
|
|
128
|
+
// Try exact match first (fast path)
|
|
129
|
+
for (const dir of projectDirs) {
|
|
130
|
+
const filePath = path.join(dir, `${id}.jsonl`);
|
|
131
|
+
try {
|
|
132
|
+
await fs.access(filePath);
|
|
133
|
+
return { filePath, resolvedId: id };
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Not in this directory
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Fall back to prefix match
|
|
140
|
+
const matches = [];
|
|
141
|
+
for (const dir of projectDirs) {
|
|
142
|
+
let dirEntries;
|
|
143
|
+
try {
|
|
144
|
+
dirEntries = await fs.readdir(dir, { withFileTypes: true, encoding: 'utf-8' });
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
for (const e of dirEntries) {
|
|
150
|
+
if (e.isFile() && e.name.endsWith('.jsonl') && e.name.startsWith(id)) {
|
|
151
|
+
const resolvedId = e.name.slice(0, -'.jsonl'.length);
|
|
152
|
+
matches.push({ filePath: path.join(dir, e.name), resolvedId });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (matches.length === 1) {
|
|
157
|
+
return matches[0];
|
|
158
|
+
}
|
|
159
|
+
// No match or ambiguous (multiple matches)
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Parse a JSONL session file into an AgentSession.
|
|
164
|
+
*
|
|
165
|
+
* Throws on any JSON parse failure so the caller can skip the file.
|
|
166
|
+
*/
|
|
167
|
+
async parseSessionFile(fileInfo, options) {
|
|
168
|
+
const content = await fs.readFile(fileInfo.filePath, 'utf-8');
|
|
169
|
+
const lines = content.split('\n').filter((l) => l.trim().length > 0);
|
|
170
|
+
let cwd;
|
|
171
|
+
let firstMessageAt;
|
|
172
|
+
let lastMessageAt;
|
|
173
|
+
let preview;
|
|
174
|
+
let messageCount = 0;
|
|
175
|
+
const messages = [];
|
|
176
|
+
// Extra metadata tracking
|
|
177
|
+
let cliVersion;
|
|
178
|
+
let gitBranch;
|
|
179
|
+
let permissionMode;
|
|
180
|
+
let userType;
|
|
181
|
+
const toolUsage = {};
|
|
182
|
+
let userMessageCount = 0;
|
|
183
|
+
let assistantMessageCount = 0;
|
|
184
|
+
for (const line of lines) {
|
|
185
|
+
// JSON.parse throws on invalid JSON — propagates to caller which skips the file
|
|
186
|
+
const entry = JSON.parse(line);
|
|
187
|
+
if (!cwd && typeof entry.cwd === 'string') {
|
|
188
|
+
cwd = entry.cwd;
|
|
189
|
+
}
|
|
190
|
+
if (!cliVersion && typeof entry.version === 'string') {
|
|
191
|
+
cliVersion = entry.version;
|
|
192
|
+
}
|
|
193
|
+
if (!gitBranch && typeof entry.gitBranch === 'string') {
|
|
194
|
+
gitBranch = entry.gitBranch;
|
|
195
|
+
}
|
|
196
|
+
if (!permissionMode && typeof entry.permissionMode === 'string') {
|
|
197
|
+
permissionMode = entry.permissionMode;
|
|
198
|
+
}
|
|
199
|
+
if (!userType && typeof entry.userType === 'string') {
|
|
200
|
+
userType = entry.userType;
|
|
201
|
+
}
|
|
202
|
+
if (entry.type === 'user' || entry.type === 'assistant') {
|
|
203
|
+
const message = entry.message;
|
|
204
|
+
const role = message?.role;
|
|
205
|
+
if (role === 'user' || role === 'assistant') {
|
|
206
|
+
messageCount++;
|
|
207
|
+
if (role === 'user')
|
|
208
|
+
userMessageCount++;
|
|
209
|
+
if (role === 'assistant')
|
|
210
|
+
assistantMessageCount++;
|
|
211
|
+
const timestamp = entry.timestamp ? new Date(entry.timestamp) : fileInfo.mtime;
|
|
212
|
+
firstMessageAt ??= timestamp;
|
|
213
|
+
lastMessageAt = timestamp;
|
|
214
|
+
if (entry.type === 'user' && preview === undefined) {
|
|
215
|
+
preview = this.extractTextContent(message?.content);
|
|
216
|
+
}
|
|
217
|
+
// Track tool usage from assistant messages
|
|
218
|
+
if (role === 'assistant' && Array.isArray(message?.content)) {
|
|
219
|
+
for (const block of message.content) {
|
|
220
|
+
if (block?.type === 'tool_use' && typeof block.name === 'string') {
|
|
221
|
+
toolUsage[block.name] = (toolUsage[block.name] ?? 0) + 1;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (options.includeMessages) {
|
|
226
|
+
messages.push({
|
|
227
|
+
uuid: entry.uuid ?? '',
|
|
228
|
+
role: role,
|
|
229
|
+
content: this.extractTextContent(message?.content),
|
|
230
|
+
timestamp,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (cwd === undefined) {
|
|
237
|
+
// Could not determine project path — file is too sparse to be useful
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
let messagesToReturn = messages;
|
|
241
|
+
if (options.includeMessages && options.messageLimit !== undefined && options.messageLimit > 0) {
|
|
242
|
+
messagesToReturn = messages.slice(-options.messageLimit);
|
|
243
|
+
}
|
|
244
|
+
const session = {
|
|
245
|
+
id: fileInfo.id,
|
|
246
|
+
agentType: 'claude-code',
|
|
247
|
+
projectPath: this.abbreviatePath(cwd),
|
|
248
|
+
messageCount,
|
|
249
|
+
createdAt: firstMessageAt ?? fileInfo.mtime,
|
|
250
|
+
updatedAt: lastMessageAt ?? fileInfo.mtime,
|
|
251
|
+
};
|
|
252
|
+
if (preview !== undefined) {
|
|
253
|
+
session.preview = preview;
|
|
254
|
+
}
|
|
255
|
+
if (firstMessageAt !== undefined) {
|
|
256
|
+
session.firstMessageAt = firstMessageAt;
|
|
257
|
+
}
|
|
258
|
+
if (lastMessageAt !== undefined) {
|
|
259
|
+
session.lastMessageAt = lastMessageAt;
|
|
260
|
+
}
|
|
261
|
+
if (options.includeMessages) {
|
|
262
|
+
session.messages = messagesToReturn;
|
|
263
|
+
session.metadata = {
|
|
264
|
+
cliVersion,
|
|
265
|
+
gitBranch,
|
|
266
|
+
permissionMode,
|
|
267
|
+
userType,
|
|
268
|
+
toolUsage,
|
|
269
|
+
userMessageCount,
|
|
270
|
+
assistantMessageCount,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return session;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Extract plain text from message content.
|
|
277
|
+
* - string content: returned as-is
|
|
278
|
+
* - array content: concatenates all text blocks; falls back to tool_use summary
|
|
279
|
+
*/
|
|
280
|
+
extractTextContent(content) {
|
|
281
|
+
if (typeof content === 'string') {
|
|
282
|
+
return content;
|
|
283
|
+
}
|
|
284
|
+
if (Array.isArray(content)) {
|
|
285
|
+
// Collect all text blocks
|
|
286
|
+
const textParts = [];
|
|
287
|
+
const toolNames = [];
|
|
288
|
+
for (const block of content) {
|
|
289
|
+
if (typeof block !== 'object' || block === null)
|
|
290
|
+
continue;
|
|
291
|
+
const b = block;
|
|
292
|
+
if (b.type === 'text' && typeof b.text === 'string') {
|
|
293
|
+
textParts.push(b.text);
|
|
294
|
+
}
|
|
295
|
+
else if (b.type === 'tool_use' && typeof b.name === 'string') {
|
|
296
|
+
toolNames.push(b.name);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (textParts.length > 0) {
|
|
300
|
+
return textParts.join('\n');
|
|
301
|
+
}
|
|
302
|
+
// No text blocks — summarize tool usage
|
|
303
|
+
if (toolNames.length > 0) {
|
|
304
|
+
return `[${toolNames.join(', ')}]`;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return '';
|
|
308
|
+
}
|
|
309
|
+
/** Replace home directory prefix with ~ in a file path */
|
|
310
|
+
abbreviatePath(filePath) {
|
|
311
|
+
const home = os.homedir();
|
|
312
|
+
if (filePath === home)
|
|
313
|
+
return '~';
|
|
314
|
+
if (filePath.startsWith(`${home}/`)) {
|
|
315
|
+
return `~${filePath.slice(home.length)}`;
|
|
316
|
+
}
|
|
317
|
+
return filePath;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
ClaudeCodeSessionRepository = __decorate([
|
|
321
|
+
injectable(),
|
|
322
|
+
__metadata("design:paramtypes", [String])
|
|
323
|
+
], ClaudeCodeSessionRepository);
|
|
324
|
+
export { ClaudeCodeSessionRepository };
|
package/dist/packages/core/src/infrastructure/services/agents/sessions/stub-session.repository.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stub Session Repository
|
|
3
|
+
*
|
|
4
|
+
* Placeholder implementation of IAgentSessionRepository for agent providers
|
|
5
|
+
* that do not yet have a real session-listing implementation (Cursor, Gemini CLI).
|
|
6
|
+
*
|
|
7
|
+
* isSupported() returns false, signalling to the use case that a warning should
|
|
8
|
+
* be emitted and an empty result returned.
|
|
9
|
+
*/
|
|
10
|
+
import type { AgentSession, AgentType } from '../../../../domain/generated/output.js';
|
|
11
|
+
import type { IAgentSessionRepository, ListSessionsOptions, GetSessionOptions } from '../../../../application/ports/output/agents/agent-session-repository.interface.js';
|
|
12
|
+
export declare class StubSessionRepository implements IAgentSessionRepository {
|
|
13
|
+
private readonly agentType;
|
|
14
|
+
constructor(agentType: AgentType);
|
|
15
|
+
isSupported(): boolean;
|
|
16
|
+
list(_options?: ListSessionsOptions): Promise<AgentSession[]>;
|
|
17
|
+
findById(_id: string, _options?: GetSessionOptions): Promise<AgentSession | null>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=stub-session.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stub-session.repository.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/sessions/stub-session.repository.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AACtF,OAAO,KAAK,EACV,uBAAuB,EACvB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,mFAAmF,CAAC;AAE3F,qBACa,qBAAsB,YAAW,uBAAuB;IACvD,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,SAAS;IAEjD,WAAW,IAAI,OAAO;IAIhB,IAAI,CAAC,QAAQ,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAI7D,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;CAGxF"}
|
package/dist/packages/core/src/infrastructure/services/agents/sessions/stub-session.repository.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stub Session Repository
|
|
3
|
+
*
|
|
4
|
+
* Placeholder implementation of IAgentSessionRepository for agent providers
|
|
5
|
+
* that do not yet have a real session-listing implementation (Cursor, Gemini CLI).
|
|
6
|
+
*
|
|
7
|
+
* isSupported() returns false, signalling to the use case that a warning should
|
|
8
|
+
* be emitted and an empty result returned.
|
|
9
|
+
*/
|
|
10
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
11
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
12
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
13
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
14
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
15
|
+
};
|
|
16
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
17
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
18
|
+
};
|
|
19
|
+
import { injectable } from 'tsyringe';
|
|
20
|
+
let StubSessionRepository = class StubSessionRepository {
|
|
21
|
+
agentType;
|
|
22
|
+
constructor(agentType) {
|
|
23
|
+
this.agentType = agentType;
|
|
24
|
+
}
|
|
25
|
+
isSupported() {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
async list(_options) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
async findById(_id, _options) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
StubSessionRepository = __decorate([
|
|
36
|
+
injectable(),
|
|
37
|
+
__metadata("design:paramtypes", [String])
|
|
38
|
+
], StubSessionRepository);
|
|
39
|
+
export { StubSessionRepository };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Command
|
|
3
|
+
*
|
|
4
|
+
* Top-level session command with subcommands for listing and inspecting
|
|
5
|
+
* agent provider CLI sessions.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* shep session [subcommand]
|
|
9
|
+
*
|
|
10
|
+
* Subcommands:
|
|
11
|
+
* shep session ls List agent provider CLI sessions
|
|
12
|
+
* shep session show <id> Display details of a specific session
|
|
13
|
+
*/
|
|
14
|
+
import { Command } from 'commander';
|
|
15
|
+
/**
|
|
16
|
+
* Create the session command with ls and show subcommands
|
|
17
|
+
*/
|
|
18
|
+
export declare function createSessionCommand(): Command;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAK9C"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Command
|
|
3
|
+
*
|
|
4
|
+
* Top-level session command with subcommands for listing and inspecting
|
|
5
|
+
* agent provider CLI sessions.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* shep session [subcommand]
|
|
9
|
+
*
|
|
10
|
+
* Subcommands:
|
|
11
|
+
* shep session ls List agent provider CLI sessions
|
|
12
|
+
* shep session show <id> Display details of a specific session
|
|
13
|
+
*/
|
|
14
|
+
import { Command } from 'commander';
|
|
15
|
+
import { createLsCommand } from './ls.command.js';
|
|
16
|
+
import { createShowCommand } from './show.command.js';
|
|
17
|
+
/**
|
|
18
|
+
* Create the session command with ls and show subcommands
|
|
19
|
+
*/
|
|
20
|
+
export function createSessionCommand() {
|
|
21
|
+
return new Command('session')
|
|
22
|
+
.description('Manage and view agent provider CLI sessions')
|
|
23
|
+
.addCommand(createLsCommand())
|
|
24
|
+
.addCommand(createShowCommand());
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session List Command
|
|
3
|
+
*
|
|
4
|
+
* List agent provider CLI sessions for the configured or specified provider.
|
|
5
|
+
* Sessions are grouped by project path and sorted by last activity within each group.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* shep session ls
|
|
9
|
+
* shep session ls --claude-code
|
|
10
|
+
* shep session ls --cursor-cli
|
|
11
|
+
* shep session ls --gemini-cli
|
|
12
|
+
* shep session ls --limit 10
|
|
13
|
+
* shep session ls --flat
|
|
14
|
+
*/
|
|
15
|
+
import { Command } from 'commander';
|
|
16
|
+
import type { AgentSession } from '../../../../../packages/core/src/domain/generated/output.js';
|
|
17
|
+
export declare function createLsCommand(): Command;
|
|
18
|
+
export declare function formatRelativeTime(date: Date | string | undefined): string;
|
|
19
|
+
export type { AgentSession };
|
|
20
|
+
//# sourceMappingURL=ls.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ls.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/session/ls.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAGjE,wBAAgB,eAAe,IAAI,OAAO,CA4CzC;AA0HD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAgB1E;AAOD,YAAY,EAAE,YAAY,EAAE,CAAC"}
|