lsd-pi 1.1.2 → 1.1.3

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.
Files changed (183) hide show
  1. package/README.md +2 -1
  2. package/dist/bedrock-auth.d.ts +25 -0
  3. package/dist/bedrock-auth.js +59 -0
  4. package/dist/headless.js +8 -3
  5. package/dist/loader.js +1 -0
  6. package/dist/onboarding-llm.d.ts +37 -0
  7. package/dist/onboarding-llm.js +64 -0
  8. package/dist/onboarding.d.ts +2 -14
  9. package/dist/onboarding.js +146 -71
  10. package/dist/pi-migration.js +1 -0
  11. package/dist/resources/extensions/memory/auto-extract.js +21 -3
  12. package/dist/resources/extensions/memory/dream.js +703 -0
  13. package/dist/resources/extensions/memory/extension-manifest.json +2 -2
  14. package/dist/resources/extensions/memory/index.js +115 -8
  15. package/dist/resources/extensions/slash-commands/extension-manifest.json +10 -10
  16. package/dist/resources/extensions/slash-commands/index.js +0 -4
  17. package/dist/resources/extensions/slash-commands/plan.js +181 -45
  18. package/dist/resources/extensions/subagent/agents.js +14 -1
  19. package/dist/resources/extensions/subagent/configured-model.js +3 -2
  20. package/dist/resources/extensions/subagent/index.js +34 -28
  21. package/dist/resources/extensions/subagent/launch-helpers.js +24 -0
  22. package/dist/resources/extensions/subagent/model-resolution.js +41 -3
  23. package/dist/resources/extensions/usage/extension-manifest.json +11 -0
  24. package/dist/resources/extensions/usage/index.js +346 -0
  25. package/{src/resources/skills/create-gsd-extension → dist/resources/skills/create-lsd-extension}/SKILL.md +6 -6
  26. package/{src/resources/skills/create-gsd-extension → dist/resources/skills/create-lsd-extension}/references/custom-tools.md +1 -1
  27. package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extension-lifecycle.md +2 -2
  28. package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensioncontext-reference.md +1 -1
  29. package/{src/resources/skills/create-gsd-extension → dist/resources/skills/create-lsd-extension}/references/key-rules-gotchas.md +4 -4
  30. package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/packaging-distribution.md +6 -6
  31. package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/create-extension.md +3 -3
  32. package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/debug-extension.md +5 -5
  33. package/dist/resources/skills/teams-debug/SKILL.md +5 -6
  34. package/dist/resources/skills/teams-document/SKILL.md +1 -2
  35. package/dist/resources/skills/teams-plan/SKILL.md +3 -4
  36. package/dist/resources/skills/teams-run/SKILL.md +3 -4
  37. package/dist/resources/skills/teams-verify/SKILL.md +4 -5
  38. package/dist/startup-model-validation.js +1 -0
  39. package/dist/welcome-screen.js +13 -11
  40. package/dist/wizard.js +12 -0
  41. package/package.json +1 -1
  42. package/packages/pi-ai/dist/models.generated.d.ts +688 -409
  43. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  44. package/packages/pi-ai/dist/models.generated.js +761 -488
  45. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  46. package/packages/pi-ai/scripts/generate-models.ts +40 -18
  47. package/packages/pi-ai/src/models.generated.ts +759 -486
  48. package/packages/pi-coding-agent/dist/cli/config-selector.js +1 -1
  49. package/packages/pi-coding-agent/dist/cli/config-selector.js.map +1 -1
  50. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +1 -2
  51. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  52. package/packages/pi-coding-agent/dist/core/agent-session.js +6 -30
  53. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  54. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
  55. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  56. package/packages/pi-coding-agent/dist/core/settings-manager.js +44 -1
  57. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  58. package/packages/pi-coding-agent/dist/core/skill-tool.test.js +9 -5
  59. package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
  60. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  61. package/packages/pi-coding-agent/dist/core/skills.js +3 -2
  62. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  63. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  64. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  65. package/packages/pi-coding-agent/dist/index.js +1 -1
  66. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  67. package/packages/pi-coding-agent/dist/main.js +1 -1
  68. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  69. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +2 -2
  70. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
  71. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  72. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +15 -12
  73. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  74. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +6 -0
  75. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  76. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +25 -1
  77. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  78. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +1 -1
  79. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  80. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  81. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +10 -0
  82. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  83. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -1
  84. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
  85. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  86. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +31 -22
  87. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  88. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +18 -5
  89. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  90. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +139 -20
  91. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  92. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  93. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +4 -0
  94. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  95. package/packages/pi-coding-agent/package.json +1 -1
  96. package/packages/pi-coding-agent/src/cli/config-selector.ts +1 -1
  97. package/packages/pi-coding-agent/src/core/agent-session.ts +5 -28
  98. package/packages/pi-coding-agent/src/core/settings-manager.ts +52 -1
  99. package/packages/pi-coding-agent/src/core/skill-tool.test.ts +18 -5
  100. package/packages/pi-coding-agent/src/core/skills.ts +3 -2
  101. package/packages/pi-coding-agent/src/index.ts +1 -1
  102. package/packages/pi-coding-agent/src/main.ts +1 -1
  103. package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +2 -2
  104. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +12 -13
  105. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +39 -1
  106. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1 -1
  107. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +10 -0
  108. package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -1
  109. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +46 -20
  110. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +171 -20
  111. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +4 -0
  112. package/packages/pi-tui/dist/components/editor.d.ts +1 -0
  113. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  114. package/packages/pi-tui/dist/components/editor.js +23 -0
  115. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  116. package/packages/pi-tui/src/components/editor.ts +23 -0
  117. package/pkg/dist/modes/interactive/theme/theme.d.ts +18 -5
  118. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  119. package/pkg/dist/modes/interactive/theme/theme.js +139 -20
  120. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  121. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  122. package/pkg/dist/modes/interactive/theme/themes.js +4 -0
  123. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  124. package/pkg/package.json +1 -1
  125. package/src/resources/extensions/memory/auto-extract.ts +23 -3
  126. package/src/resources/extensions/memory/dream.ts +814 -0
  127. package/src/resources/extensions/memory/extension-manifest.json +2 -2
  128. package/src/resources/extensions/memory/index.ts +134 -13
  129. package/src/resources/extensions/memory/tests/auto-extract.test.ts +10 -2
  130. package/src/resources/extensions/memory/tests/dream.test.ts +142 -0
  131. package/src/resources/extensions/slash-commands/extension-manifest.json +10 -10
  132. package/src/resources/extensions/slash-commands/index.ts +3 -7
  133. package/src/resources/extensions/slash-commands/plan.ts +192 -46
  134. package/src/resources/extensions/subagent/agents.ts +11 -1
  135. package/src/resources/extensions/subagent/configured-model.ts +3 -2
  136. package/src/resources/extensions/subagent/index.ts +38 -30
  137. package/src/resources/extensions/subagent/launch-helpers.ts +30 -0
  138. package/src/resources/extensions/subagent/model-resolution.ts +40 -3
  139. package/src/resources/extensions/usage/extension-manifest.json +11 -0
  140. package/src/resources/extensions/usage/index.ts +441 -0
  141. package/{dist/resources/skills/create-gsd-extension → src/resources/skills/create-lsd-extension}/SKILL.md +6 -6
  142. package/{dist/resources/skills/create-gsd-extension → src/resources/skills/create-lsd-extension}/references/custom-tools.md +1 -1
  143. package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extension-lifecycle.md +2 -2
  144. package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensioncontext-reference.md +1 -1
  145. package/{dist/resources/skills/create-gsd-extension → src/resources/skills/create-lsd-extension}/references/key-rules-gotchas.md +4 -4
  146. package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/packaging-distribution.md +6 -6
  147. package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/create-extension.md +3 -3
  148. package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/debug-extension.md +5 -5
  149. package/src/resources/skills/teams-debug/SKILL.md +5 -6
  150. package/src/resources/skills/teams-document/SKILL.md +1 -2
  151. package/src/resources/skills/teams-plan/SKILL.md +3 -4
  152. package/src/resources/skills/teams-run/SKILL.md +3 -4
  153. package/src/resources/skills/teams-verify/SKILL.md +4 -5
  154. package/dist/resources/extensions/slash-commands/create-extension.js +0 -264
  155. package/dist/resources/extensions/slash-commands/create-slash-command.js +0 -208
  156. package/src/resources/extensions/slash-commands/create-extension.ts +0 -297
  157. package/src/resources/extensions/slash-commands/create-slash-command.ts +0 -234
  158. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/compaction-session-control.md +0 -0
  159. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-commands.md +0 -0
  160. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-rendering.md +0 -0
  161. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-ui.md +0 -0
  162. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/events-reference.md +0 -0
  163. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensionapi-reference.md +0 -0
  164. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/mode-behavior.md +0 -0
  165. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/model-provider-management.md +0 -0
  166. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/remote-execution-overrides.md +0 -0
  167. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/state-management.md +0 -0
  168. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/references/system-prompt-modification.md +0 -0
  169. /package/dist/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/add-capability.md +0 -0
  170. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/compaction-session-control.md +0 -0
  171. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-commands.md +0 -0
  172. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-rendering.md +0 -0
  173. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/custom-ui.md +0 -0
  174. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/events-reference.md +0 -0
  175. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/extensionapi-reference.md +0 -0
  176. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/mode-behavior.md +0 -0
  177. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/model-provider-management.md +0 -0
  178. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/remote-execution-overrides.md +0 -0
  179. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/state-management.md +0 -0
  180. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/references/system-prompt-modification.md +0 -0
  181. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/templates/extension-skeleton.ts +0 -0
  182. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/templates/stateful-tool-skeleton.ts +0 -0
  183. /package/src/resources/skills/{create-gsd-extension → create-lsd-extension}/workflows/add-capability.md +0 -0
@@ -8,18 +8,55 @@ export interface ResolveSubagentModelOptions {
8
8
  parentModel?: { provider: string; id: string };
9
9
  }
10
10
 
11
+ const BARE_MODEL_PROVIDER_RULES: Array<{ provider: string; matches: (modelId: string) => boolean }> = [
12
+ { provider: "anthropic", matches: (modelId) => modelId.startsWith("claude-") },
13
+ { provider: "google", matches: (modelId) => modelId.startsWith("gemini-") },
14
+ {
15
+ provider: "openai",
16
+ matches: (modelId) => /^(gpt-|o[134]-|omni-|text-embedding-)/.test(modelId),
17
+ },
18
+ { provider: "xai", matches: (modelId) => modelId.startsWith("grok-") },
19
+ {
20
+ provider: "mistral",
21
+ matches: (modelId) => /^(mistral-|ministral-|codestral-)/.test(modelId),
22
+ },
23
+ { provider: "groq", matches: (modelId) => modelId.startsWith("llama-") || modelId.startsWith("mixtral-") },
24
+ ];
25
+
26
+ export function inferProviderForBareModel(modelId: string): string | undefined {
27
+ const normalizedModelId = modelId.trim().toLowerCase();
28
+ if (!normalizedModelId) return undefined;
29
+ return BARE_MODEL_PROVIDER_RULES.find((rule) => rule.matches(normalizedModelId))?.provider;
30
+ }
31
+
32
+ export function isQualifiedSubagentModel(model: string): boolean {
33
+ const trimmed = model.trim();
34
+ if (!trimmed || trimmed.includes(" ")) return false;
35
+ const parts = trimmed.split("/");
36
+ return parts.length === 2 && parts.every(Boolean);
37
+ }
38
+
39
+ export function normalizeSubagentModel(model: string | undefined | null): string | undefined {
40
+ const trimmed = model?.trim();
41
+ if (!trimmed || trimmed.startsWith("$")) return undefined;
42
+ if (isQualifiedSubagentModel(trimmed)) return trimmed;
43
+ if (trimmed.includes("/")) return undefined;
44
+ const inferredProvider = inferProviderForBareModel(trimmed);
45
+ return inferredProvider ? `${inferredProvider}/${trimmed}` : undefined;
46
+ }
47
+
11
48
  export function resolveSubagentModel(
12
49
  agent: ResolvedSubagentAgent,
13
50
  options: ResolveSubagentModelOptions = {},
14
51
  ): string | undefined {
15
- const overrideModel = options.overrideModel?.trim();
52
+ const overrideModel = normalizeSubagentModel(options.overrideModel);
16
53
  if (overrideModel) return overrideModel;
17
54
 
18
- const agentModel = agent.model?.trim();
55
+ const agentModel = normalizeSubagentModel(agent.model);
19
56
  if (agentModel) return agentModel;
20
57
 
21
58
  if (options.parentModel?.provider && options.parentModel?.id) {
22
- return `${options.parentModel.provider}/${options.parentModel.id}`;
59
+ return normalizeSubagentModel(`${options.parentModel.provider}/${options.parentModel.id}`);
23
60
  }
24
61
 
25
62
  return undefined;
@@ -0,0 +1,11 @@
1
+ {
2
+ "id": "usage",
3
+ "name": "Usage",
4
+ "version": "1.0.0",
5
+ "description": "Show built-in LSD token and cost usage reports aggregated from session history",
6
+ "tier": "bundled",
7
+ "requires": { "platform": ">=2.29.0" },
8
+ "provides": {
9
+ "commands": ["usage"]
10
+ }
11
+ }
@@ -0,0 +1,441 @@
1
+ import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
2
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
3
+ import { basename, join } from "node:path";
4
+ import { homedir } from "node:os";
5
+
6
+ type GroupBy = "model" | "project" | "project-model";
7
+ type Scope = "all-projects" | "current-project";
8
+
9
+ function getSessionsRoot(): string {
10
+ const appRoot = process.env.LSD_HOME || join(homedir(), ".lsd");
11
+ return join(appRoot, "sessions");
12
+ }
13
+
14
+ function getCurrentProjectSessionsDir(cwd: string): string {
15
+ const safePath = `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
16
+ return join(getSessionsRoot(), safePath);
17
+ }
18
+
19
+ type UsageRow = {
20
+ key: string;
21
+ project: string;
22
+ model: string;
23
+ messages: number;
24
+ input: number;
25
+ output: number;
26
+ cacheRead: number;
27
+ cacheWrite: number;
28
+ total: number;
29
+ cost: number;
30
+ };
31
+
32
+ type UsageReport = {
33
+ label: string;
34
+ scope: Scope;
35
+ groupBy: GroupBy;
36
+ sessionsRoot: string;
37
+ filesScanned: number;
38
+ matchedMessages: number;
39
+ rows: UsageRow[];
40
+ totals: Omit<UsageRow, "key" | "project" | "model">;
41
+ };
42
+
43
+ type ParsedArgs = {
44
+ label: string;
45
+ startMs: number;
46
+ endMs: number;
47
+ scope: Scope;
48
+ groupBy: GroupBy;
49
+ json: boolean;
50
+ };
51
+
52
+ type SessionHeaderLike = {
53
+ type?: string;
54
+ cwd?: string;
55
+ };
56
+
57
+ type AssistantMessageLike = {
58
+ role?: string;
59
+ provider?: string;
60
+ model?: string;
61
+ timestamp?: number;
62
+ usage?: {
63
+ input?: number;
64
+ output?: number;
65
+ cacheRead?: number;
66
+ cacheWrite?: number;
67
+ cost?: { total?: number };
68
+ };
69
+ };
70
+
71
+ function startOfLocalDay(date: Date): number {
72
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
73
+ }
74
+
75
+ function endOfLocalDay(date: Date): number {
76
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1).getTime();
77
+ }
78
+
79
+ function formatInt(value: number): string {
80
+ return Math.round(value).toLocaleString();
81
+ }
82
+
83
+ function formatCost(value: number): string {
84
+ return `$${value.toFixed(4)}`;
85
+ }
86
+
87
+ function parseRangeToken(token: string | undefined): { label: string; startMs: number; endMs: number } {
88
+ const trimmed = token?.trim();
89
+ if (!trimmed || trimmed === "today") {
90
+ const now = new Date();
91
+ return {
92
+ label: "today",
93
+ startMs: startOfLocalDay(now),
94
+ endMs: endOfLocalDay(now),
95
+ };
96
+ }
97
+
98
+ const rollingMatch = trimmed.match(/^(\d+)d$/i);
99
+ if (rollingMatch) {
100
+ const days = Math.max(1, Number.parseInt(rollingMatch[1] ?? "1", 10));
101
+ const now = new Date();
102
+ return {
103
+ label: `${days}d`,
104
+ startMs: startOfLocalDay(new Date(now.getFullYear(), now.getMonth(), now.getDate() - (days - 1))),
105
+ endMs: endOfLocalDay(now),
106
+ };
107
+ }
108
+
109
+ const dateMatch = trimmed.match(/^(\d{4})-(\d{2})-(\d{2})$/);
110
+ if (dateMatch) {
111
+ const year = Number.parseInt(dateMatch[1] ?? "0", 10);
112
+ const month = Number.parseInt(dateMatch[2] ?? "1", 10) - 1;
113
+ const day = Number.parseInt(dateMatch[3] ?? "1", 10);
114
+ const date = new Date(year, month, day);
115
+ if (!Number.isNaN(date.getTime())) {
116
+ return {
117
+ label: trimmed,
118
+ startMs: startOfLocalDay(date),
119
+ endMs: endOfLocalDay(date),
120
+ };
121
+ }
122
+ }
123
+
124
+ throw new Error(`Invalid usage range "${trimmed}". Use: today, 7d, or YYYY-MM-DD.`);
125
+ }
126
+
127
+ function parseArgs(rawArgs: string): ParsedArgs {
128
+ const tokens = rawArgs.trim().split(/\s+/).filter(Boolean);
129
+ let rangeToken: string | undefined;
130
+ let scope: Scope = "all-projects";
131
+ let groupBy: GroupBy = "model";
132
+ let json = false;
133
+
134
+ for (let i = 0; i < tokens.length; i++) {
135
+ const token = tokens[i];
136
+ if (token === "--project-current") {
137
+ scope = "current-project";
138
+ continue;
139
+ }
140
+ if (token === "--all-projects") {
141
+ scope = "all-projects";
142
+ continue;
143
+ }
144
+ if (token === "--json") {
145
+ json = true;
146
+ continue;
147
+ }
148
+ if (token === "--by") {
149
+ const next = tokens[i + 1];
150
+ if (next !== "model" && next !== "project" && next !== "project-model") {
151
+ throw new Error('Invalid --by value. Use: model, project, or project-model.');
152
+ }
153
+ groupBy = next;
154
+ i++;
155
+ continue;
156
+ }
157
+ if (token.startsWith("--")) {
158
+ throw new Error(`Unknown flag: ${token}`);
159
+ }
160
+ if (!rangeToken) {
161
+ rangeToken = token;
162
+ continue;
163
+ }
164
+ throw new Error(`Unexpected argument: ${token}`);
165
+ }
166
+
167
+ const range = parseRangeToken(rangeToken);
168
+ return {
169
+ ...range,
170
+ scope,
171
+ groupBy,
172
+ json,
173
+ };
174
+ }
175
+
176
+ function walkJsonlFiles(dir: string): string[] {
177
+ if (!existsSync(dir)) return [];
178
+ const out: string[] = [];
179
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
180
+ const fullPath = join(dir, entry.name);
181
+ if (entry.isDirectory()) {
182
+ out.push(...walkJsonlFiles(fullPath));
183
+ continue;
184
+ }
185
+ if (entry.isFile() && fullPath.endsWith(".jsonl")) {
186
+ out.push(fullPath);
187
+ }
188
+ }
189
+ return out;
190
+ }
191
+
192
+ function normalizeProjectLabel(cwd: string | undefined, fallbackPath: string): string {
193
+ if (cwd && cwd.trim()) return cwd;
194
+ return basename(fallbackPath);
195
+ }
196
+
197
+ function makeModelLabel(message: AssistantMessageLike): string {
198
+ if (message.provider && message.model) return `${message.provider}/${message.model}`;
199
+ if (message.model) return message.model;
200
+ return "unknown";
201
+ }
202
+
203
+ function makeGroupKey(groupBy: GroupBy, project: string, model: string): string {
204
+ switch (groupBy) {
205
+ case "project":
206
+ return project;
207
+ case "project-model":
208
+ return `${project} :: ${model}`;
209
+ case "model":
210
+ default:
211
+ return model;
212
+ }
213
+ }
214
+
215
+ function collectUsage(sessionFiles: string[], startMs: number, endMs: number, scope: Scope, groupBy: GroupBy): UsageReport {
216
+ const rows = new Map<string, UsageRow>();
217
+ let filesScanned = 0;
218
+ let matchedMessages = 0;
219
+
220
+ for (const file of sessionFiles) {
221
+ filesScanned++;
222
+ let projectLabel = basename(file);
223
+ let headerResolved = false;
224
+
225
+ const raw = readFileSync(file, "utf-8");
226
+ for (const line of raw.split("\n")) {
227
+ if (!line.trim()) continue;
228
+ let parsed: any;
229
+ try {
230
+ parsed = JSON.parse(line);
231
+ } catch {
232
+ continue;
233
+ }
234
+
235
+ if (!headerResolved) {
236
+ const header = parsed as SessionHeaderLike;
237
+ if (header.type === "session") {
238
+ projectLabel = normalizeProjectLabel(header.cwd, file);
239
+ headerResolved = true;
240
+ }
241
+ }
242
+
243
+ const message = parsed?.type === "message" ? (parsed.message as AssistantMessageLike | undefined) : undefined;
244
+ if (!message || message.role !== "assistant") continue;
245
+
246
+ const timestamp = Number(message.timestamp ?? 0);
247
+ if (!timestamp || timestamp < startMs || timestamp >= endMs) continue;
248
+
249
+ matchedMessages++;
250
+ const model = makeModelLabel(message);
251
+ const key = makeGroupKey(groupBy, projectLabel, model);
252
+ const usage = message.usage ?? {};
253
+ const input = Number(usage.input ?? 0);
254
+ const output = Number(usage.output ?? 0);
255
+ const cacheRead = Number(usage.cacheRead ?? 0);
256
+ const cacheWrite = Number(usage.cacheWrite ?? 0);
257
+ const cost = Number(usage.cost?.total ?? 0);
258
+ const total = input + output + cacheRead + cacheWrite;
259
+
260
+ const existing = rows.get(key) ?? {
261
+ key,
262
+ project: groupBy === "model" ? "—" : projectLabel,
263
+ model: groupBy === "project" ? "—" : model,
264
+ messages: 0,
265
+ input: 0,
266
+ output: 0,
267
+ cacheRead: 0,
268
+ cacheWrite: 0,
269
+ total: 0,
270
+ cost: 0,
271
+ };
272
+
273
+ existing.messages += 1;
274
+ existing.input += input;
275
+ existing.output += output;
276
+ existing.cacheRead += cacheRead;
277
+ existing.cacheWrite += cacheWrite;
278
+ existing.total += total;
279
+ existing.cost += cost;
280
+ rows.set(key, existing);
281
+ }
282
+ }
283
+
284
+ const orderedRows = [...rows.values()].sort((left, right) => {
285
+ if (right.total !== left.total) return right.total - left.total;
286
+ return left.key.localeCompare(right.key);
287
+ });
288
+
289
+ const totals = orderedRows.reduce(
290
+ (acc, row) => {
291
+ acc.messages += row.messages;
292
+ acc.input += row.input;
293
+ acc.output += row.output;
294
+ acc.cacheRead += row.cacheRead;
295
+ acc.cacheWrite += row.cacheWrite;
296
+ acc.total += row.total;
297
+ acc.cost += row.cost;
298
+ return acc;
299
+ },
300
+ { messages: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0, cost: 0 },
301
+ );
302
+
303
+ return {
304
+ label: "",
305
+ scope,
306
+ groupBy,
307
+ sessionsRoot: getSessionsRoot(),
308
+ filesScanned,
309
+ matchedMessages,
310
+ rows: orderedRows,
311
+ totals,
312
+ };
313
+ }
314
+
315
+ function renderTable(report: UsageReport): string {
316
+ const firstColumnHeader = report.groupBy === "project"
317
+ ? "project"
318
+ : report.groupBy === "project-model"
319
+ ? "project :: model"
320
+ : "model";
321
+
322
+ const displayRows = report.rows.map((row) => ({
323
+ label: row.key,
324
+ msgs: String(row.messages),
325
+ input: formatInt(row.input),
326
+ output: formatInt(row.output),
327
+ read: formatInt(row.cacheRead),
328
+ write: formatInt(row.cacheWrite),
329
+ total: formatInt(row.total),
330
+ cost: formatCost(row.cost),
331
+ }));
332
+
333
+ const widths = {
334
+ label: Math.max(firstColumnHeader.length, ...displayRows.map((row) => row.label.length), 5),
335
+ msgs: Math.max(4, ...displayRows.map((row) => row.msgs.length), String(report.totals.messages).length),
336
+ input: Math.max(5, ...displayRows.map((row) => row.input.length), formatInt(report.totals.input).length),
337
+ output: Math.max(6, ...displayRows.map((row) => row.output.length), formatInt(report.totals.output).length),
338
+ read: Math.max(4, ...displayRows.map((row) => row.read.length), formatInt(report.totals.cacheRead).length),
339
+ write: Math.max(5, ...displayRows.map((row) => row.write.length), formatInt(report.totals.cacheWrite).length),
340
+ total: Math.max(5, ...displayRows.map((row) => row.total.length), formatInt(report.totals.total).length),
341
+ cost: Math.max(7, ...displayRows.map((row) => row.cost.length), formatCost(report.totals.cost).length),
342
+ };
343
+
344
+ const header = [
345
+ firstColumnHeader.padEnd(widths.label),
346
+ "msgs".padStart(widths.msgs),
347
+ "input".padStart(widths.input),
348
+ "output".padStart(widths.output),
349
+ "read".padStart(widths.read),
350
+ "write".padStart(widths.write),
351
+ "total".padStart(widths.total),
352
+ "cost".padStart(widths.cost),
353
+ ].join(" ");
354
+
355
+ const divider = "-".repeat(header.length);
356
+ const body = displayRows.map((row) => [
357
+ row.label.padEnd(widths.label),
358
+ row.msgs.padStart(widths.msgs),
359
+ row.input.padStart(widths.input),
360
+ row.output.padStart(widths.output),
361
+ row.read.padStart(widths.read),
362
+ row.write.padStart(widths.write),
363
+ row.total.padStart(widths.total),
364
+ row.cost.padStart(widths.cost),
365
+ ].join(" "));
366
+
367
+ const totalsLine = [
368
+ "TOTAL".padEnd(widths.label),
369
+ String(report.totals.messages).padStart(widths.msgs),
370
+ formatInt(report.totals.input).padStart(widths.input),
371
+ formatInt(report.totals.output).padStart(widths.output),
372
+ formatInt(report.totals.cacheRead).padStart(widths.read),
373
+ formatInt(report.totals.cacheWrite).padStart(widths.write),
374
+ formatInt(report.totals.total).padStart(widths.total),
375
+ formatCost(report.totals.cost).padStart(widths.cost),
376
+ ].join(" ");
377
+
378
+ return [header, divider, ...body, divider, totalsLine].join("\n");
379
+ }
380
+
381
+ function renderReport(report: UsageReport): string {
382
+ const scopeLabel = report.scope === "all-projects" ? "all projects" : "current project";
383
+ const summary = [
384
+ `Usage report: ${report.label}`,
385
+ `Scope: ${scopeLabel}`,
386
+ `Grouped by: ${report.groupBy}`,
387
+ `Sessions root: ${report.sessionsRoot}`,
388
+ `Session files scanned: ${report.filesScanned}`,
389
+ `Assistant messages matched: ${report.matchedMessages}`,
390
+ ].join("\n");
391
+
392
+ if (report.rows.length === 0) {
393
+ return `${summary}\n\nNo assistant usage found for that range.`;
394
+ }
395
+
396
+ return `${summary}\n\n\
397
+ \`\`\`text\n${renderTable(report)}\n\`\`\``;
398
+ }
399
+
400
+ function buildUsageReport(ctx: ExtensionCommandContext, args: ParsedArgs): UsageReport {
401
+ const sessionFiles = args.scope === "current-project"
402
+ ? walkJsonlFiles(getCurrentProjectSessionsDir(ctx.cwd))
403
+ : walkJsonlFiles(getSessionsRoot());
404
+ const report = collectUsage(sessionFiles, args.startMs, args.endMs, args.scope, args.groupBy);
405
+ report.label = args.label;
406
+ return report;
407
+ }
408
+
409
+ export default function usageExtension(pi: ExtensionAPI) {
410
+ pi.registerCommand("usage", {
411
+ description: "Show token and cost usage from LSD sessions (today by model by default)",
412
+ async handler(rawArgs: string, ctx: ExtensionCommandContext) {
413
+ try {
414
+ const parsed = parseArgs(rawArgs);
415
+ const report = buildUsageReport(ctx, parsed);
416
+ const content = parsed.json
417
+ ? `\`\`\`json\n${JSON.stringify(report, null, 2)}\n\`\`\``
418
+ : renderReport(report);
419
+
420
+ pi.sendMessage({
421
+ customType: "usage-report",
422
+ content,
423
+ display: true,
424
+ });
425
+ } catch (error) {
426
+ const message = error instanceof Error ? error.message : "Unknown error";
427
+ ctx.ui.notify(
428
+ `${message}\nUsage: /usage [today|7d|YYYY-MM-DD] [--project-current|--all-projects] [--by model|project|project-model] [--json]`,
429
+ "error",
430
+ );
431
+ }
432
+ },
433
+ });
434
+ }
435
+
436
+ export const __testing = {
437
+ parseArgs,
438
+ parseRangeToken,
439
+ renderTable,
440
+ collectUsage,
441
+ };
@@ -1,17 +1,17 @@
1
1
  ---
2
- name: create-gsd-extension
3
- description: Create, debug, and iterate on GSD extensions (TypeScript modules that add tools, commands, event hooks, custom UI, and providers to GSD). Use when asked to build an extension, add a tool the LLM can call, register a slash command, hook into GSD events, create custom TUI components, or modify GSD behavior. Triggers on "create extension", "build extension", "add a tool", "register command", "hook into gsd", "custom tool", "gsd plugin", "gsd extension".
2
+ name: create-lsd-extension
3
+ description: Create, debug, and iterate on LSD extensions (TypeScript modules that add tools, commands, event hooks, custom UI, and providers to LSD). Use when asked to build an extension, add a tool the LLM can call, register a slash command, hook into LSD events, create custom TUI components, or modify LSD behavior. Triggers on "create extension", "build extension", "add a tool", "register command", "hook into lsd", "custom tool", "lsd plugin", "lsd extension".
4
4
  ---
5
5
 
6
6
  <essential_principles>
7
7
 
8
- **Extensions are TypeScript modules** that hook into GSD's runtime (built on pi). They export a default function receiving `ExtensionAPI` and use it to subscribe to events, register tools/commands/shortcuts, and interact with the session.
8
+ **Extensions are TypeScript modules** that hook into LSD's runtime (built on pi). They export a default function receiving `ExtensionAPI` and use it to subscribe to events, register tools/commands/shortcuts, and interact with the session.
9
9
 
10
- **GSD extension paths (community/user-installed extensions):**
10
+ **LSD extension paths (community/user-installed extensions):**
11
11
  - Global: `~/.pi/agent/extensions/*.ts` or `~/.pi/agent/extensions/*/index.ts`
12
12
  - Project-local: `.gsd/extensions/*.ts` or `.gsd/extensions/*/index.ts`
13
13
 
14
- Note: `~/.gsd/agent/extensions/` is reserved for bundled extensions synced from the gsd-pi package. Community extensions placed there are silently ignored by the loader.
14
+ Note: `~/.gsd/agent/extensions/` is reserved for bundled extensions synced from the lsd-pi package. Community extensions placed there are silently ignored by the loader.
15
15
 
16
16
  **The three primitives:**
17
17
  1. **Events** — Listen and react (`pi.on("event", handler)`). Can block tool calls, modify messages, inject context.
@@ -79,7 +79,7 @@ All domain knowledge in `references/`:
79
79
  <success_criteria>
80
80
  Extension is complete when:
81
81
  - TypeScript compiles without errors (jiti handles this at runtime)
82
- - Extension loads on GSD startup or `/reload` without errors
82
+ - Extension loads on LSD startup or `/reload` without errors
83
83
  - Tools appear in the LLM's system prompt and are callable
84
84
  - Commands respond to `/command` input
85
85
  - Event hooks fire at the expected lifecycle points
@@ -149,7 +149,7 @@ pi.registerTool({
149
149
  });
150
150
  ```
151
151
 
152
- Start with no built-in tools: `gsd --no-tools -e ./my-extension.ts`
152
+ Start with no built-in tools: `lsd --no-tools -e ./my-extension.ts`
153
153
  </overriding_builtins>
154
154
 
155
155
  <multiple_tools>
@@ -3,10 +3,10 @@ The extension lifecycle from load to shutdown, including the full event flow.
3
3
  </overview>
4
4
 
5
5
  <loading>
6
- Extensions load when GSD starts (or on `/reload`). The default export function runs synchronously — subscribe to events and register tools/commands during this call.
6
+ Extensions load when LSD starts (or on `/reload`). The default export function runs synchronously — subscribe to events and register tools/commands during this call.
7
7
 
8
8
  ```
9
- GSD starts
9
+ LSD starts
10
10
  └─► Extension default function runs
11
11
  ├── pi.on("event", handler) ← Subscribe
12
12
  ├── pi.registerTool({...}) ← Register tools
@@ -21,7 +21,7 @@ ctx.ui.setStatus("my-ext", "● Active"); // Footer status
21
21
  ctx.ui.setStatus("my-ext", undefined); // Clear
22
22
  ctx.ui.setWidget("my-id", ["Line 1", "Line 2"]); // Widget above editor
23
23
  ctx.ui.setWidget("my-id", ["Below!"], { placement: "belowEditor" });
24
- ctx.ui.setTitle("gsd - my project"); // Terminal title
24
+ ctx.ui.setTitle("lsd - my project"); // Terminal title
25
25
  ctx.ui.setEditorText("Prefill"); // Set editor content
26
26
  ctx.ui.setWorkingMessage("Analyzing..."); // Working message during streaming
27
27
  ctx.ui.setToolsExpanded(true); // Expand tool output
@@ -1,5 +1,5 @@
1
1
  <overview>
2
- Non-negotiable rules and common gotchas when building GSD extensions.
2
+ Non-negotiable rules and common gotchas when building LSD extensions.
3
3
  </overview>
4
4
 
5
5
  <must_follow>
@@ -22,16 +22,16 @@ Non-negotiable rules and common gotchas when building GSD extensions.
22
22
  - Overlay components are **disposed when closed** — create fresh instances each time
23
23
  - Treat `ctx.reload()` as terminal — code after runs from pre-reload version
24
24
  - Check `ctx.hasUI` before dialog methods (false in print/JSON mode)
25
- - Extension errors are logged but don't crash GSD — tool_call handler errors fail-safe (block the tool)
25
+ - Extension errors are logged but don't crash LSD — tool_call handler errors fail-safe (block the tool)
26
26
  </common_patterns>
27
27
 
28
28
  <gsd_paths>
29
- **GSD extension paths (community/user-installed extensions):**
29
+ **LSD extension paths (community/user-installed extensions):**
30
30
  - Global: `~/.pi/agent/extensions/*.ts`
31
31
  - Global (subdir): `~/.pi/agent/extensions/*/index.ts`
32
32
  - Project-local: `.gsd/extensions/*.ts`
33
33
  - Project-local (subdir): `.gsd/extensions/*/index.ts`
34
34
 
35
- Note: `~/.gsd/agent/extensions/` is reserved for bundled extensions synced from the gsd-pi package.
35
+ Note: `~/.gsd/agent/extensions/` is reserved for bundled extensions synced from the lsd-pi package.
36
36
  Community extensions placed there are silently ignored by the loader.
37
37
  </gsd_paths>
@@ -1,5 +1,5 @@
1
1
  <overview>
2
- Packaging extensions for distribution via npm, git, or local paths. Creating GSD/pi packages.
2
+ Packaging extensions for distribution via npm, git, or local paths. Creating LSD/pi packages.
3
3
  </overview>
4
4
 
5
5
  <package_manifest>
@@ -7,7 +7,7 @@ Add a `pi` manifest to `package.json`:
7
7
 
8
8
  ```json
9
9
  {
10
- "name": "my-gsd-package",
10
+ "name": "my-lsd-package",
11
11
  "keywords": ["pi-package"],
12
12
  "pi": {
13
13
  "extensions": ["./extensions"],
@@ -21,12 +21,12 @@ Add a `pi` manifest to `package.json`:
21
21
 
22
22
  <installing>
23
23
  ```bash
24
- gsd install npm:@foo/bar@1.0.0
25
- gsd install git:github.com/user/repo@v1
26
- gsd install ./local/path
24
+ lsd install npm:@foo/bar@1.0.0
25
+ lsd install git:github.com/user/repo@v1
26
+ lsd install ./local/path
27
27
 
28
28
  # Try without installing:
29
- gsd -e npm:@foo/bar
29
+ lsd -e npm:@foo/bar
30
30
  ```
31
31
  </installing>
32
32
 
@@ -12,7 +12,7 @@
12
12
  ## Step 1: Determine Scope and Placement
13
13
 
14
14
  Ask the user:
15
- - **Global** (`~/.pi/agent/extensions/`) — Available in all GSD sessions
15
+ - **Global** (`~/.pi/agent/extensions/`) — Available in all LSD sessions
16
16
  - **Project-local** (`.gsd/extensions/`) — Available only in this project
17
17
 
18
18
  ## Step 2: Determine Extension Capabilities
@@ -124,14 +124,14 @@ pi.on("tool_call", async (event, ctx) => {
124
124
 
125
125
  ```bash
126
126
  # Quick test without installing
127
- gsd -e ./path/to/my-extension.ts
127
+ lsd -e ./path/to/my-extension.ts
128
128
 
129
129
  # Or place in extensions dir and reload
130
130
  /reload
131
131
  ```
132
132
 
133
133
  Verify:
134
- - Extension loads without errors (check GSD startup output)
134
+ - Extension loads without errors (check LSD startup output)
135
135
  - Tools appear when LLM is asked to use them
136
136
  - Commands respond to `/mycommand`
137
137
  - Event hooks trigger at expected points