@zigai/pi-mention-project 0.2.0 → 0.2.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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@zigai/pi-mention-project.svg)](https://www.npmjs.com/package/@zigai/pi-mention-project)
5
5
  [![license](https://img.shields.io/npm/l/@zigai/pi-mention-project.svg)](../../LICENSE)
6
6
 
7
- This Pi extension adds fuzzy project directory mentions that default to `#`.
7
+ Type `#` to fuzzy-search your project folder. The completion inserts `#project-name`; the model receives the project's absolute path.
8
8
 
9
9
  ## Install
10
10
 
@@ -12,65 +12,31 @@ This Pi extension adds fuzzy project directory mentions that default to `#`.
12
12
  pi install npm:@zigai/pi-mention-project
13
13
  ```
14
14
 
15
- ## Features
15
+ ## Config
16
16
 
17
- - Adds fuzzy autocomplete for project folders with `#` mentions.
18
- - Searches only the direct child folders inside configured project roots.
19
- - Defaults to Git repository folders and ignores dot-prefixed folders.
20
- - Expands project mentions before the model sees the prompt so the model gets each project's absolute path.
21
-
22
- ## Usage
23
-
24
- Configure one or more project roots, then type `#` in the prompt editor and start typing a folder name.
25
-
26
- If `~/Projects` contains `pi-tweaks`, selecting `#pi-tweaks` adds that project mention. Before the model sees the prompt, the extension prepends project metadata with the absolute project path and removes the `#` sigil from the visible sentence.
27
-
28
- ## Configuration
29
-
30
- Configuration lives in Pi settings: globally in `~/.pi/agent/settings.json`, or per trusted project in `.pi/settings.json`.
31
-
32
- Project roots default to an empty list. Set `mentionProjectRoots` to the directories whose direct child folders should be mentionable:
33
-
34
- ```json
35
- {
36
- "mentionProjectRoots": ["~/Projects", "~/Work"]
37
- }
38
- ```
39
-
40
- Only immediate child directories are listed. For example, `~/Projects/app` is mentionable, but `~/Projects/app/packages/api` is not.
41
-
42
- By default, a child folder is listed only when it has a `.git` directory or worktree `.git` file, and folders whose names start with `.` are hidden. To include non-Git folders, set `mentionProjectGitReposOnly` to `false`:
17
+ Add roots in `~/.pi/agent/settings.json` or `.pi/settings.json`:
43
18
 
44
19
  ```json
45
20
  {
46
- "mentionProjectGitReposOnly": false
21
+ "mentionProjectRoots": ["~/Projects"]
47
22
  }
48
23
  ```
49
24
 
50
- To include dot-prefixed folders, set `mentionProjectIncludeDotFolders` to `true`:
25
+ By default it lists only direct child folders that:
51
26
 
52
- ```json
53
- {
54
- "mentionProjectIncludeDotFolders": true
55
- }
56
- ```
27
+ - are Git repos (`.git` directory or worktree file)
28
+ - do not start with `.`
57
29
 
58
- For one-off runs, the same filters can be relaxed with CLI flags:
59
-
60
- ```sh
61
- pi --mention-project-include-non-git --mention-project-include-dot-folders
62
- ```
63
-
64
- The mention character defaults to `#`. To change it, set `mentionProjectTrigger` to a single non-whitespace character:
30
+ Optional settings:
65
31
 
66
32
  ```json
67
33
  {
68
- "mentionProjectTrigger": "@"
34
+ "mentionProjectTrigger": "#",
35
+ "mentionProjectGitReposOnly": true,
36
+ "mentionProjectIncludeDotFolders": false
69
37
  }
70
38
  ```
71
39
 
72
- Folder names should be unique across configured roots. If the same folder name exists in multiple roots, the first configured root wins.
73
-
74
40
  ## License
75
41
 
76
42
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zigai/pi-mention-project",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Pi package that adds fuzzy project directory mentions.",
5
5
  "keywords": [
6
6
  "mention",
@@ -7,31 +7,6 @@ import {
7
7
  } from "./mention-syntax.ts";
8
8
  import type { ProjectDirectory } from "./types.ts";
9
9
 
10
- function escapeXmlText(value: string): string {
11
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
12
- }
13
-
14
- function escapeXmlAttribute(value: string): string {
15
- return escapeXmlText(value).replace(/"/g, "&quot;");
16
- }
17
-
18
- function formatProjectBlock(project: ProjectDirectory): string {
19
- const name = escapeXmlAttribute(project.name);
20
- const projectPath = escapeXmlAttribute(project.path);
21
- const root = escapeXmlAttribute(project.root);
22
- return `<project name="${name}" path="${projectPath}" root="${root}">\nDirectory: ${escapeXmlText(project.path)}\n</project>`;
23
- }
24
-
25
- function formatCombinedProjectBlock(projects: ProjectDirectory[]): string {
26
- if (projects.length === 1) {
27
- const project = projects[0];
28
- if (project !== undefined) return formatProjectBlock(project);
29
- }
30
-
31
- const content = projects.map((project) => formatProjectBlock(project)).join("\n\n");
32
- return `<projects>\n${content}\n</projects>`;
33
- }
34
-
35
10
  function projectMap(projects: ProjectDirectory[]): Map<string, ProjectDirectory> {
36
11
  const byName = new Map<string, ProjectDirectory>();
37
12
  for (const project of projects) {
@@ -41,69 +16,36 @@ function projectMap(projects: ProjectDirectory[]): Map<string, ProjectDirectory>
41
16
  return byName;
42
17
  }
43
18
 
44
- function mentionedProjectNames(
45
- text: string,
46
- projects: ProjectDirectory[],
47
- trigger: string,
48
- ): Set<string> {
49
- const names = new Set<string>();
50
- const knownNames = projectNameSet(projects);
51
-
52
- for (const match of text.matchAll(projectMentionPattern(trigger))) {
53
- const parsed = parseProjectMentionName(match[2], match[3], knownNames);
54
- if (parsed !== undefined) {
55
- names.add(parsed.name);
56
- }
57
- }
58
-
59
- return names;
60
- }
61
-
62
- function removeProjectMentionSigils(
63
- text: string,
64
- projects: ProjectDirectory[],
65
- trigger: string,
66
- ): string {
67
- const knownNames = projectNameSet(projects);
68
- return text
69
- .replace(
70
- projectMentionPattern(trigger),
71
- (
72
- match: string,
73
- leading: string,
74
- quotedName: string | undefined,
75
- unquotedName: string | undefined,
76
- ) => {
77
- const parsed = parseProjectMentionName(quotedName, unquotedName, knownNames);
78
- if (parsed === undefined) return match;
79
- return `${leading}${parsed.name}${parsed.suffix}`;
80
- },
81
- )
82
- .trim();
83
- }
84
-
85
19
  export function expandProjectMentions(
86
20
  text: string,
87
21
  projects: ProjectDirectory[],
88
22
  trigger: string,
89
23
  ): string {
90
24
  const byName = projectMap(projects);
91
- const names = mentionedProjectNames(text, projects, trigger);
92
- if (names.size === 0) return text;
93
-
94
- const loaded: ProjectDirectory[] = [];
95
- for (const name of names) {
96
- const project = byName.get(name);
97
- if (project !== undefined) {
98
- loaded.push(project);
99
- }
100
- }
101
- if (loaded.length === 0) return text;
25
+ const knownNames = projectNameSet(projects);
26
+ let changed = false;
102
27
 
103
- const projectBlock = formatCombinedProjectBlock(loaded);
104
- const userMessage = removeProjectMentionSigils(text, projects, trigger);
105
- if (userMessage.length === 0) return projectBlock;
106
- return `${projectBlock}\n\n${userMessage}`;
28
+ const expanded = text.replace(
29
+ projectMentionPattern(trigger),
30
+ (
31
+ match: string,
32
+ leading: string,
33
+ quotedName: string | undefined,
34
+ unquotedName: string | undefined,
35
+ ) => {
36
+ const parsed = parseProjectMentionName(quotedName, unquotedName, knownNames);
37
+ if (parsed === undefined) return match;
38
+
39
+ const project = byName.get(parsed.name);
40
+ if (project === undefined) return match;
41
+
42
+ changed = true;
43
+ return `${leading}${project.path}${parsed.suffix}`;
44
+ },
45
+ );
46
+
47
+ if (!changed) return text;
48
+ return expanded;
107
49
  }
108
50
 
109
51
  type ContextMessage = ContextEvent["messages"][number];