pi-auto-session-title 0.1.0 → 0.1.2

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/index.ts ADDED
@@ -0,0 +1,97 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+
3
+ function textFromContent(content: unknown): string {
4
+ if (typeof content === "string") return content;
5
+ if (!Array.isArray(content)) return "";
6
+
7
+ return content
8
+ .filter(
9
+ (block): block is { type: "text"; text: string } =>
10
+ typeof block === "object" &&
11
+ block !== null &&
12
+ (block as { type?: unknown }).type === "text" &&
13
+ typeof (block as { text?: unknown }).text === "string",
14
+ )
15
+ .map((block) => block.text)
16
+ .join("\n");
17
+ }
18
+
19
+ function cleanTitle(raw: string): string | undefined {
20
+ const title = raw
21
+ .split("\n")
22
+ .map((line) => line.trim())
23
+ .find(Boolean)
24
+ ?.replace(/^(?:session\s+)?title\s*:\s*/i, "")
25
+ .replace(/^#{1,6}\s*/, "")
26
+ .replace(/^["'`]+|["'`]+$/g, "")
27
+ .replace(/[.!?]+$/g, "")
28
+ .trim();
29
+
30
+ if (!title) return undefined;
31
+ return title.length <= 72 ? title : `${title.slice(0, 71).trimEnd()}…`;
32
+ }
33
+
34
+ export default function (pi: ExtensionAPI) {
35
+ let attempted = false;
36
+
37
+ pi.on("session_start", () => {
38
+ attempted = false;
39
+ });
40
+
41
+ pi.on("agent_settled", async (_event, ctx) => {
42
+ if (attempted || pi.getSessionName() || !ctx.model) return;
43
+ attempted = true;
44
+
45
+ const conversation = ctx.sessionManager
46
+ .getBranch()
47
+ .filter(
48
+ (
49
+ entry,
50
+ ): entry is typeof entry & {
51
+ message: { role: string; content?: unknown };
52
+ } =>
53
+ entry.type === "message" &&
54
+ "role" in entry.message &&
55
+ (entry.message.role === "user" || entry.message.role === "assistant"),
56
+ )
57
+ .slice(-10)
58
+ .map((entry) => {
59
+ const text = textFromContent(entry.message.content)
60
+ .trim()
61
+ .slice(0, 2_000);
62
+ return `${entry.message.role}: ${text}`;
63
+ })
64
+ .filter(Boolean)
65
+ .join("\n\n")
66
+ .slice(0, 12_000);
67
+
68
+ if (!conversation) return;
69
+
70
+ try {
71
+ const response = await ctx.modelRegistry.complete(
72
+ ctx.model,
73
+ {
74
+ systemPrompt:
75
+ "Write a specific, searchable 3-7 word title for this coding session. " +
76
+ "Return only the title with no quotes, markdown, or prefix.",
77
+ messages: [
78
+ {
79
+ role: "user",
80
+ content: [{ type: "text", text: conversation }],
81
+ timestamp: Date.now(),
82
+ },
83
+ ],
84
+ },
85
+ { maxTokens: 80, signal: AbortSignal.timeout(30_000) },
86
+ );
87
+
88
+ const title = cleanTitle(textFromContent(response.content));
89
+ if (title && !pi.getSessionName()) {
90
+ pi.setSessionName(title);
91
+ if (ctx.hasUI) ctx.ui.notify(`Session named: ${title}`, "info");
92
+ }
93
+ } catch (error) {
94
+ console.error("[auto-session-title]", error);
95
+ }
96
+ });
97
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-auto-session-title",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Automatically generates concise, descriptive session titles for Pi coding agent sessions",
5
5
  "author": "Shakthi Sagar",
6
6
  "license": "MIT",
@@ -14,7 +14,7 @@
14
14
  "url": "https://github.com/shakthi-sagar/pi-extensions/issues"
15
15
  },
16
16
  "type": "module",
17
- "main": "./src/index.ts",
17
+ "main": "./index.ts",
18
18
  "keywords": [
19
19
  "pi-package",
20
20
  "pi-extension",
@@ -25,7 +25,7 @@
25
25
  "files": [
26
26
  "LICENSE",
27
27
  "README.md",
28
- "src",
28
+ "index.ts",
29
29
  "tsconfig.json"
30
30
  ],
31
31
  "scripts": {
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "pi": {
35
35
  "extensions": [
36
- "./src/index.ts"
36
+ "./index.ts"
37
37
  ]
38
38
  },
39
39
  "peerDependencies": {
package/tsconfig.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "extends": "../../tsconfig.base.json",
3
3
  "compilerOptions": {
4
- "rootDir": "./src"
4
+ "rootDir": "."
5
5
  },
6
- "include": ["src/**/*.ts"]
6
+ "include": ["index.ts"]
7
7
  }
package/src/index.ts DELETED
@@ -1,91 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
-
3
- function textFromContent(content: unknown): string {
4
- if (typeof content === "string") return content;
5
- if (!Array.isArray(content)) return "";
6
-
7
- return content
8
- .filter(
9
- (block): block is { type: "text"; text: string } =>
10
- typeof block === "object" &&
11
- block !== null &&
12
- (block as { type?: unknown }).type === "text" &&
13
- typeof (block as { text?: unknown }).text === "string",
14
- )
15
- .map((block) => block.text)
16
- .join("\n");
17
- }
18
-
19
- function cleanTitle(raw: string): string | undefined {
20
- const title = raw
21
- .split("\n")
22
- .map((line) => line.trim())
23
- .find(Boolean)
24
- ?.replace(/^(?:session\s+)?title\s*:\s*/i, "")
25
- .replace(/^#{1,6}\s*/, "")
26
- .replace(/^["'`]+|["'`]+$/g, "")
27
- .replace(/[.!?]+$/g, "")
28
- .trim();
29
-
30
- if (!title) return undefined;
31
- return title.length <= 72 ? title : `${title.slice(0, 71).trimEnd()}…`;
32
- }
33
-
34
- export default function (pi: ExtensionAPI) {
35
- let attempted = false;
36
-
37
- pi.on("session_start", () => {
38
- attempted = false;
39
- });
40
-
41
- pi.on("agent_settled", async (_event, ctx) => {
42
- if (attempted || pi.getSessionName() || !ctx.model) return;
43
- attempted = true;
44
-
45
- const conversation = ctx.sessionManager
46
- .getBranch()
47
- .filter(
48
- (entry): entry is typeof entry & { message: { role: string; content?: unknown } } =>
49
- entry.type === "message" &&
50
- "role" in entry.message &&
51
- (entry.message.role === "user" || entry.message.role === "assistant"),
52
- )
53
- .slice(-10)
54
- .map((entry) => {
55
- const text = textFromContent(entry.message.content).trim().slice(0, 2_000);
56
- return `${entry.message.role}: ${text}`;
57
- })
58
- .filter(Boolean)
59
- .join("\n\n")
60
- .slice(0, 12_000);
61
-
62
- if (!conversation) return;
63
-
64
- try {
65
- const response = await ctx.modelRegistry.complete(
66
- ctx.model,
67
- {
68
- systemPrompt:
69
- "Write a specific, searchable 3-7 word title for this coding session. " +
70
- "Return only the title with no quotes, markdown, or prefix.",
71
- messages: [
72
- {
73
- role: "user",
74
- content: [{ type: "text", text: conversation }],
75
- timestamp: Date.now(),
76
- },
77
- ],
78
- },
79
- { maxTokens: 80, signal: AbortSignal.timeout(30_000) },
80
- );
81
-
82
- const title = cleanTitle(textFromContent(response.content));
83
- if (title && !pi.getSessionName()) {
84
- pi.setSessionName(title);
85
- if (ctx.hasUI) ctx.ui.notify(`Session named: ${title}`, "info");
86
- }
87
- } catch (error) {
88
- console.error("[auto-session-title]", error);
89
- }
90
- });
91
- }