@kubbisec/ai-linux-arm64 0.1.8 → 0.1.10
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/bin/kubbisec +0 -0
- package/bundled/assets/terminal/wordmark-inline.ansi +1 -0
- package/bundled/assets/terminal/wordmark.ansi +4 -0
- package/bundled/assets/terminal/wordmark.png +0 -0
- package/bundled/clarify/clarify-gate-state.d.ts +3 -0
- package/bundled/clarify/clarify-gate-state.d.ts.map +1 -0
- package/bundled/clarify/clarify-gate-state.js +9 -0
- package/bundled/clarify/clarify-gate-state.js.map +1 -0
- package/bundled/clarify/clarify-gate.d.ts +2 -1
- package/bundled/clarify/clarify-gate.d.ts.map +1 -1
- package/bundled/clarify/clarify-gate.js +17 -4
- package/bundled/clarify/clarify-gate.js.map +1 -1
- package/bundled/clarify/clarify-select-panel.d.ts +14 -0
- package/bundled/clarify/clarify-select-panel.d.ts.map +1 -0
- package/bundled/clarify/clarify-select-panel.js +82 -0
- package/bundled/clarify/clarify-select-panel.js.map +1 -0
- package/bundled/clarify/clarify-ui-state.d.ts +10 -1
- package/bundled/clarify/clarify-ui-state.d.ts.map +1 -1
- package/bundled/clarify/clarify-ui-state.js +24 -3
- package/bundled/clarify/clarify-ui-state.js.map +1 -1
- package/bundled/clarify/parse-clarify-answer.d.ts +6 -1
- package/bundled/clarify/parse-clarify-answer.d.ts.map +1 -1
- package/bundled/clarify/parse-clarify-answer.js +35 -2
- package/bundled/clarify/parse-clarify-answer.js.map +1 -1
- package/bundled/clarify/question-format.d.ts +7 -4
- package/bundled/clarify/question-format.d.ts.map +1 -1
- package/bundled/clarify/question-format.js +26 -33
- package/bundled/clarify/question-format.js.map +1 -1
- package/bundled/clarify/questionnaire-tool.d.ts.map +1 -1
- package/bundled/clarify/questionnaire-tool.js +16 -9
- package/bundled/clarify/questionnaire-tool.js.map +1 -1
- package/bundled/clarify/scope-discovery.d.ts +10 -0
- package/bundled/clarify/scope-discovery.d.ts.map +1 -0
- package/bundled/clarify/scope-discovery.js +130 -0
- package/bundled/clarify/scope-discovery.js.map +1 -0
- package/bundled/clarify/session-questionnaire.d.ts +4 -1
- package/bundled/clarify/session-questionnaire.d.ts.map +1 -1
- package/bundled/clarify/session-questionnaire.js +59 -4
- package/bundled/clarify/session-questionnaire.js.map +1 -1
- package/bundled/clarify/session-questionnaire.types.d.ts +2 -0
- package/bundled/clarify/session-questionnaire.types.d.ts.map +1 -1
- package/bundled/clarify/session-questionnaire.types.js.map +1 -1
- package/bundled/config/detect-local-llm-providers.d.ts +33 -0
- package/bundled/config/detect-local-llm-providers.d.ts.map +1 -0
- package/bundled/config/detect-local-llm-providers.js +173 -0
- package/bundled/config/detect-local-llm-providers.js.map +1 -0
- package/bundled/config/ensure-kubbisec-project-settings.d.ts +8 -0
- package/bundled/config/ensure-kubbisec-project-settings.d.ts.map +1 -1
- package/bundled/config/ensure-kubbisec-project-settings.js +31 -0
- package/bundled/config/ensure-kubbisec-project-settings.js.map +1 -1
- package/bundled/config/extended-context.d.ts +12 -0
- package/bundled/config/extended-context.d.ts.map +1 -0
- package/bundled/config/extended-context.js +37 -0
- package/bundled/config/extended-context.js.map +1 -0
- package/bundled/config/format-kubbisec-footer-status.d.ts.map +1 -1
- package/bundled/config/format-kubbisec-footer-status.js +10 -1
- package/bundled/config/format-kubbisec-footer-status.js.map +1 -1
- package/bundled/config/kubbisec-extension-manifest.d.ts +3 -0
- package/bundled/config/kubbisec-extension-manifest.d.ts.map +1 -0
- package/bundled/config/kubbisec-extension-manifest.js +23 -0
- package/bundled/config/kubbisec-extension-manifest.js.map +1 -0
- package/bundled/config/llm-config-mutations.d.ts +38 -0
- package/bundled/config/llm-config-mutations.d.ts.map +1 -0
- package/bundled/config/llm-config-mutations.js +207 -0
- package/bundled/config/llm-config-mutations.js.map +1 -0
- package/bundled/config/llm-config.d.ts +64 -0
- package/bundled/config/llm-config.d.ts.map +1 -0
- package/bundled/config/llm-config.js +236 -0
- package/bundled/config/llm-config.js.map +1 -0
- package/bundled/config/llm-provider-catalog.d.ts +22 -0
- package/bundled/config/llm-provider-catalog.d.ts.map +1 -0
- package/bundled/config/llm-provider-catalog.js +42 -0
- package/bundled/config/llm-provider-catalog.js.map +1 -0
- package/bundled/config/materialize-kubbisec-pi-resources.d.ts +13 -0
- package/bundled/config/materialize-kubbisec-pi-resources.d.ts.map +1 -0
- package/bundled/config/materialize-kubbisec-pi-resources.js +171 -0
- package/bundled/config/materialize-kubbisec-pi-resources.js.map +1 -0
- package/bundled/config/register-custom-llm-providers.d.ts +13 -0
- package/bundled/config/register-custom-llm-providers.d.ts.map +1 -0
- package/bundled/config/register-custom-llm-providers.js +21 -0
- package/bundled/config/register-custom-llm-providers.js.map +1 -0
- package/bundled/config/register-kubbisec-llm-provider.d.ts +14 -0
- package/bundled/config/register-kubbisec-llm-provider.d.ts.map +1 -0
- package/bundled/config/register-kubbisec-llm-provider.js +53 -0
- package/bundled/config/register-kubbisec-llm-provider.js.map +1 -0
- package/bundled/config/sync-kubbisec-pi-models.d.ts +12 -0
- package/bundled/config/sync-kubbisec-pi-models.d.ts.map +1 -0
- package/bundled/config/sync-kubbisec-pi-models.js +80 -0
- package/bundled/config/sync-kubbisec-pi-models.js.map +1 -0
- package/bundled/copy/cli-reference-index.d.ts +6 -0
- package/bundled/copy/cli-reference-index.d.ts.map +1 -0
- package/bundled/copy/cli-reference-index.js +42 -0
- package/bundled/copy/cli-reference-index.js.map +1 -0
- package/bundled/copy/kubbisec-shell-copy.d.ts +15 -2
- package/bundled/copy/kubbisec-shell-copy.d.ts.map +1 -1
- package/bundled/copy/kubbisec-shell-copy.js +15 -2
- package/bundled/copy/kubbisec-shell-copy.js.map +1 -1
- package/bundled/extensions/cli-local-shared.d.ts +31 -11
- package/bundled/extensions/cli-local-shared.d.ts.map +1 -1
- package/bundled/extensions/cli-local-shared.js +56 -10
- package/bundled/extensions/cli-local-shared.js.map +1 -1
- package/bundled/extensions/kubbisec-aspm-local.extension.d.ts +1 -0
- package/bundled/extensions/kubbisec-aspm-local.extension.d.ts.map +1 -1
- package/bundled/extensions/kubbisec-aspm-local.extension.js +32 -44
- package/bundled/extensions/kubbisec-aspm-local.extension.js.map +1 -1
- package/bundled/extensions/kubbisec-brand.extension.d.ts +2 -19
- package/bundled/extensions/kubbisec-brand.extension.d.ts.map +1 -1
- package/bundled/extensions/kubbisec-brand.extension.js +21 -73
- package/bundled/extensions/kubbisec-brand.extension.js.map +1 -1
- package/bundled/extensions/kubbisec-byok-provider.extension.d.ts +3 -27
- package/bundled/extensions/kubbisec-byok-provider.extension.d.ts.map +1 -1
- package/bundled/extensions/kubbisec-byok-provider.extension.js +2 -48
- package/bundled/extensions/kubbisec-byok-provider.extension.js.map +1 -1
- package/bundled/extensions/kubbisec-clarify.extension.d.ts +6 -1
- package/bundled/extensions/kubbisec-clarify.extension.d.ts.map +1 -1
- package/bundled/extensions/kubbisec-clarify.extension.js +124 -68
- package/bundled/extensions/kubbisec-clarify.extension.js.map +1 -1
- package/bundled/extensions/kubbisec-exit.extension.d.ts +3 -0
- package/bundled/extensions/kubbisec-exit.extension.d.ts.map +1 -0
- package/bundled/extensions/kubbisec-exit.extension.js +38 -0
- package/bundled/extensions/kubbisec-exit.extension.js.map +1 -0
- package/bundled/extensions/kubbisec-extended-context.extension.d.ts +6 -0
- package/bundled/extensions/kubbisec-extended-context.extension.d.ts.map +1 -0
- package/bundled/extensions/kubbisec-extended-context.extension.js +50 -0
- package/bundled/extensions/kubbisec-extended-context.extension.js.map +1 -0
- package/bundled/extensions/kubbisec-footer.extension.d.ts +16 -61
- package/bundled/extensions/kubbisec-footer.extension.d.ts.map +1 -1
- package/bundled/extensions/kubbisec-footer.extension.js +143 -74
- package/bundled/extensions/kubbisec-footer.extension.js.map +1 -1
- package/bundled/extensions/kubbisec-github-local.extension.d.ts.map +1 -1
- package/bundled/extensions/kubbisec-github-local.extension.js +52 -22
- package/bundled/extensions/kubbisec-github-local.extension.js.map +1 -1
- package/bundled/extensions/kubbisec-llm-config.extension.d.ts +3 -0
- package/bundled/extensions/kubbisec-llm-config.extension.d.ts.map +1 -0
- package/bundled/extensions/kubbisec-llm-config.extension.js +211 -0
- package/bundled/extensions/kubbisec-llm-config.extension.js.map +1 -0
- package/bundled/extensions/kubbisec-naus-local.extension.d.ts +1 -0
- package/bundled/extensions/kubbisec-naus-local.extension.d.ts.map +1 -1
- package/bundled/extensions/kubbisec-naus-local.extension.js +54 -58
- package/bundled/extensions/kubbisec-naus-local.extension.js.map +1 -1
- package/bundled/extensions/kubbisec-platform-tools.extension.d.ts.map +1 -1
- package/bundled/extensions/kubbisec-platform-tools.extension.js +57 -16
- package/bundled/extensions/kubbisec-platform-tools.extension.js.map +1 -1
- package/bundled/extensions/kubbisec-skill-mentions.extension.d.ts +1 -0
- package/bundled/extensions/kubbisec-skill-mentions.extension.d.ts.map +1 -1
- package/bundled/extensions/kubbisec-skill-mentions.extension.js +24 -8
- package/bundled/extensions/kubbisec-skill-mentions.extension.js.map +1 -1
- package/bundled/extensions/kubbisec-theme-lock.extension.d.ts +6 -0
- package/bundled/extensions/kubbisec-theme-lock.extension.d.ts.map +1 -0
- package/bundled/extensions/kubbisec-theme-lock.extension.js +43 -0
- package/bundled/extensions/kubbisec-theme-lock.extension.js.map +1 -0
- package/bundled/extensions/kubbisec-ui.extension.d.ts +1 -1
- package/bundled/extensions/kubbisec-ui.extension.d.ts.map +1 -1
- package/bundled/extensions/kubbisec-ui.extension.js +98 -82
- package/bundled/extensions/kubbisec-ui.extension.js.map +1 -1
- package/bundled/extensions/kubbisec-workflow.extension.d.ts.map +1 -1
- package/bundled/extensions/kubbisec-workflow.extension.js +31 -21
- package/bundled/extensions/kubbisec-workflow.extension.js.map +1 -1
- package/bundled/skills/aspm-platform/SKILL.md +55 -25
- package/bundled/skills/at-path-mentions.d.ts +7 -0
- package/bundled/skills/at-path-mentions.d.ts.map +1 -0
- package/bundled/skills/at-path-mentions.js +186 -0
- package/bundled/skills/at-path-mentions.js.map +1 -0
- package/bundled/skills/at-path-mentions.test.ts +64 -0
- package/bundled/skills/at-path-mentions.ts +211 -0
- package/bundled/skills/chat-mention-render.d.ts.map +1 -1
- package/bundled/skills/chat-mention-render.js +69 -31
- package/bundled/skills/chat-mention-render.js.map +1 -1
- package/bundled/skills/chat-mention-render.test.ts +24 -1
- package/bundled/skills/chat-mention-render.ts +87 -34
- package/bundled/skills/chat-mentions.d.ts +3 -2
- package/bundled/skills/chat-mentions.d.ts.map +1 -1
- package/bundled/skills/chat-mentions.js +9 -3
- package/bundled/skills/chat-mentions.js.map +1 -1
- package/bundled/skills/chat-mentions.test.ts +12 -5
- package/bundled/skills/chat-mentions.ts +12 -3
- package/bundled/skills/naus-pipelines/SKILL.md +111 -16
- package/bundled/skills/sec-clarify/SKILL.md +8 -23
- package/bundled/skills/sec-using-skills/SKILL.md +2 -0
- package/bundled/theme/clear-terminal.d.ts +3 -0
- package/bundled/theme/clear-terminal.d.ts.map +1 -0
- package/bundled/theme/clear-terminal.js +7 -0
- package/bundled/theme/clear-terminal.js.map +1 -0
- package/bundled/theme/footer-render-hook.d.ts +3 -0
- package/bundled/theme/footer-render-hook.d.ts.map +1 -0
- package/bundled/theme/footer-render-hook.js +8 -0
- package/bundled/theme/footer-render-hook.js.map +1 -0
- package/bundled/theme/kubbisec-brand-fg.d.ts +4 -0
- package/bundled/theme/kubbisec-brand-fg.d.ts.map +1 -0
- package/bundled/theme/kubbisec-brand-fg.js +7 -0
- package/bundled/theme/kubbisec-brand-fg.js.map +1 -0
- package/bundled/theme/kubbisec-menu-themes.d.ts +16 -0
- package/bundled/theme/kubbisec-menu-themes.d.ts.map +1 -0
- package/bundled/theme/kubbisec-menu-themes.js +47 -0
- package/bundled/theme/kubbisec-menu-themes.js.map +1 -0
- package/bundled/theme/kubbisec-theme-name.d.ts +2 -0
- package/bundled/theme/kubbisec-theme-name.d.ts.map +1 -0
- package/bundled/theme/kubbisec-theme-name.js +2 -0
- package/bundled/theme/kubbisec-theme-name.js.map +1 -0
- package/bundled/theme/patch-pi-settings-theme.runtime.d.ts +3 -0
- package/bundled/theme/patch-pi-settings-theme.runtime.d.ts.map +1 -0
- package/bundled/theme/patch-pi-settings-theme.runtime.js +16 -0
- package/bundled/theme/patch-pi-settings-theme.runtime.js.map +1 -0
- package/bundled/theme/render-compact-header.d.ts +21 -0
- package/bundled/theme/render-compact-header.d.ts.map +1 -0
- package/bundled/theme/render-compact-header.js +198 -0
- package/bundled/theme/render-compact-header.js.map +1 -0
- package/package.json +1 -1
|
@@ -3,11 +3,58 @@ name: aspm-platform
|
|
|
3
3
|
description: KubbiSec ASPM — uploads, gates, projetos e integração com NAUS/CLI.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Instalação
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- **
|
|
8
|
+
`npm install -g @kubbisec/aspm` → binário `aspm`.
|
|
9
|
+
|
|
10
|
+
**Docs:** `aspm_docs` (secção `aspm-commands` + guias por scanner). **HARD-GATE:** chamar `aspm_docs` ou `security_scanner_playbook` antes de citar flags completas — não inventar sintaxe.
|
|
11
|
+
|
|
12
|
+
## Comandos CLI
|
|
13
|
+
|
|
14
|
+
| Comando | Quando usar |
|
|
15
|
+
|---------|-------------|
|
|
16
|
+
| `aspm init` | Setup rápido (interativo) |
|
|
17
|
+
| `aspm login` | Configurar credenciais (interativo ou flags) |
|
|
18
|
+
| `aspm whoami` | Mostrar configuração atual |
|
|
19
|
+
| `aspm doctor` | Diagnosticar configuração |
|
|
20
|
+
| `aspm scan <file> -s <source>` | Parse **local** de relatório (sem upload API) |
|
|
21
|
+
| `aspm parse <file> -s <source>` | Parse local (dry run) |
|
|
22
|
+
| `aspm upload <file> -s <source>` | **Upload** de relatório para a API |
|
|
23
|
+
| `aspm coverage <file>` | Upload de coverage (lcov) |
|
|
24
|
+
| `aspm parse-coverage <file>` | Parse coverage local |
|
|
25
|
+
| `aspm projects` | Listar projetos da organização |
|
|
26
|
+
| `aspm dashboard` | Abrir dashboard no browser |
|
|
27
|
+
|
|
28
|
+
### Flags globais
|
|
29
|
+
|
|
30
|
+
`-k/--api-key`, `-o/--org`, `-p/--project`, `-v/--version`, `-h/--help`
|
|
31
|
+
|
|
32
|
+
### Variáveis de ambiente (precedência: flags CLI > env > `~/.kubbisec/config.json`)
|
|
33
|
+
|
|
34
|
+
| Variável | Descrição |
|
|
35
|
+
|----------|-----------|
|
|
36
|
+
| `KUBBISEC_API_URL` / `ASPM_API_URL` | URL da API |
|
|
37
|
+
| `KUBBISEC_API_KEY` / `ASPM_API_KEY` | API key |
|
|
38
|
+
| `KUBBISEC_ORG_ID` / `ASPM_ORG_ID` | Organization ID |
|
|
39
|
+
| `KUBBISEC_PROJECT` / `KUBBISEC_PROJECT_ID` / `ASPM_PROJECT_ID` | Project ID |
|
|
40
|
+
|
|
41
|
+
## Scanners ↔ `-s` ↔ ficheiro de relatório
|
|
42
|
+
|
|
43
|
+
| Scanner | `-s` | Ficheiro típico |
|
|
44
|
+
|---------|------|-----------------|
|
|
45
|
+
| Trivy | `trivy` | `trivy-report.json` |
|
|
46
|
+
| Gitleaks | `gitleaks` | `gitleaks-report.json` |
|
|
47
|
+
| Syft | `syft` | `sbom.json` |
|
|
48
|
+
| OSV | `osv` | `osv-report.json` |
|
|
49
|
+
| Semgrep | `semgrep` | `semgrep-report.json` |
|
|
50
|
+
| Sonar | `sonar` | `sonar-report.json` |
|
|
51
|
+
| Nmap | `nmap` | `nmap-report.xml` |
|
|
52
|
+
| Skipfish | `skipfish` | `skipfish-report.json` |
|
|
53
|
+
|
|
54
|
+
1. `security_scanners_catalog` — capacidades e formatos.
|
|
55
|
+
2. `security_scanner_playbook(scanner, platform?, topic?)` — install, scan, upload, snippet NAUS.
|
|
56
|
+
|
|
57
|
+
**Nota:** `aspm-cicd.md` descreve **GitHub Actions** (`jobs:`, `needs:`). Para CI nativo KubbiSec, usar skill `naus-pipelines` e `naus_pipeline_docs`.
|
|
11
58
|
|
|
12
59
|
## Remediação via tools (kbs chat)
|
|
13
60
|
|
|
@@ -16,26 +63,14 @@ description: KubbiSec ASPM — uploads, gates, projetos e integração com NAUS/
|
|
|
16
63
|
3. `generate_remediation_plan` com o mesmo `vulnerabilityId`
|
|
17
64
|
4. `start_ai_remediation_run` com `vulnerabilityId` e `mode: suggestion` (ou `auto` se permitido)
|
|
18
65
|
|
|
19
|
-
Sem finding ativo na UI web, **sempre** passe `vulnerabilityId` nos args.
|
|
66
|
+
Sem finding ativo na UI web, **sempre** passe `vulnerabilityId` nos args.
|
|
20
67
|
|
|
21
68
|
## Remediar todas as vulnerabilidades por projeto
|
|
22
69
|
|
|
23
70
|
1. `kubsec_list_projects` — listar projetos da org
|
|
24
71
|
2. Para **cada** projeto: `list_findings({ projectId, severity?, limit: 50 })`
|
|
25
|
-
3. Para **cada** item
|
|
26
|
-
|
|
27
|
-
- `start_ai_remediation_run({ vulnerabilityId: item.id, mode: "suggestion" })`
|
|
28
|
-
- Acompanhar com `get_ai_remediation_run_status` / logs se necessário
|
|
29
|
-
4. Passar ao projeto seguinte
|
|
30
|
-
|
|
31
|
-
Não existe tool batch — processe sequencialmente. Confirme scope no questionário (`questionnaire`) antes de mutations em massa.
|
|
32
|
-
|
|
33
|
-
## Scanners suportados
|
|
34
|
-
|
|
35
|
-
trivy, gitleaks, syft, osv, semgrep, sonar, nmap, skipfish.
|
|
36
|
-
|
|
37
|
-
1. `security_scanners_catalog` — capacidades e ficheiros de relatório.
|
|
38
|
-
2. `security_scanner_playbook(scanner, platform?, topic?)` — install, scan, upload, NAUS.
|
|
72
|
+
3. Para **cada** item: `generate_remediation_plan` → `start_ai_remediation_run`
|
|
73
|
+
4. Não existe tool batch — processe sequencialmente.
|
|
39
74
|
|
|
40
75
|
## Local (`kbs chat --extended-context`)
|
|
41
76
|
|
|
@@ -46,12 +81,7 @@ trivy, gitleaks, syft, osv, semgrep, sonar, nmap, skipfish.
|
|
|
46
81
|
| `aspm_local_scan` | `aspm scan <file> -s <source>` |
|
|
47
82
|
| `aspm_local_upload` | `aspm upload <file> -s <source> -p <projectId>` |
|
|
48
83
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
- `KUBBISEC_API_URL` / `ASPM_API_URL`
|
|
52
|
-
- `KUBBISEC_API_KEY` / `ASPM_API_KEY`
|
|
53
|
-
- `KUBBISEC_ORG_ID` / `ASPM_ORG_ID`
|
|
54
|
-
- `KUBBISEC_PROJECT` / `KUBBISEC_PROJECT_ID` / `ASPM_PROJECT_ID`
|
|
84
|
+
`parse`, `parse-coverage`, `coverage`, `projects`, `dashboard` — usar CLI diretamente no terminal; documentar via `aspm_docs`.
|
|
55
85
|
|
|
56
86
|
## NAUS
|
|
57
87
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AutocompleteItem } from "@earendil-works/pi-tui";
|
|
2
|
+
/** `@` file attachment (not `@skill` / `@plan`). */
|
|
3
|
+
export declare function isAtFileAttachmentPrefix(atPrefix: string | null): boolean;
|
|
4
|
+
/** List cwd-scoped entries for `@` / `@path` (Pi getFileSuggestions semantics). */
|
|
5
|
+
export declare function getAtPathFileSuggestions(cwd: string, atPrefix: string): AutocompleteItem[];
|
|
6
|
+
export declare function extractAtFilePrefix(textBeforeCursor: string): string | null;
|
|
7
|
+
//# sourceMappingURL=at-path-mentions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"at-path-mentions.d.ts","sourceRoot":"","sources":["../../src/skills/at-path-mentions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAmH/D,oDAAoD;AACpD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAKzE;AAED,mFAAmF;AACnF,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE,CA+E1F;AAED,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG3E"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, relative } from "node:path";
|
|
3
|
+
import { extractAtPrefix, parsePlanMentionPrefix, parseSkillMentionPrefix, } from "./chat-mentions.js";
|
|
4
|
+
function toDisplayPath(value) {
|
|
5
|
+
return value.replace(/\\/g, "/");
|
|
6
|
+
}
|
|
7
|
+
function parsePathPrefix(prefix) {
|
|
8
|
+
if (prefix.startsWith('@"')) {
|
|
9
|
+
return { rawPrefix: prefix.slice(2), isAtPrefix: true, isQuotedPrefix: true };
|
|
10
|
+
}
|
|
11
|
+
if (prefix.startsWith('"')) {
|
|
12
|
+
return { rawPrefix: prefix.slice(1), isAtPrefix: false, isQuotedPrefix: true };
|
|
13
|
+
}
|
|
14
|
+
if (prefix.startsWith("@")) {
|
|
15
|
+
return { rawPrefix: prefix.slice(1), isAtPrefix: true, isQuotedPrefix: false };
|
|
16
|
+
}
|
|
17
|
+
return { rawPrefix: prefix, isAtPrefix: false, isQuotedPrefix: false };
|
|
18
|
+
}
|
|
19
|
+
function buildCompletionValue(path, options) {
|
|
20
|
+
const needsQuotes = options.isQuotedPrefix || path.includes(" ");
|
|
21
|
+
const prefix = options.isAtPrefix ? "@" : "";
|
|
22
|
+
if (!needsQuotes) {
|
|
23
|
+
return `${prefix}${path}`;
|
|
24
|
+
}
|
|
25
|
+
const openQuote = `${prefix}"`;
|
|
26
|
+
return `${openQuote}${path}"`;
|
|
27
|
+
}
|
|
28
|
+
const DEPRIORITIZED_AT_PATH_NAMES = new Set([
|
|
29
|
+
"node_modules",
|
|
30
|
+
"dist",
|
|
31
|
+
"dist-bin",
|
|
32
|
+
".turbo",
|
|
33
|
+
".git",
|
|
34
|
+
"coverage",
|
|
35
|
+
"build",
|
|
36
|
+
".next",
|
|
37
|
+
".cache",
|
|
38
|
+
"tmp",
|
|
39
|
+
".venv",
|
|
40
|
+
"target",
|
|
41
|
+
]);
|
|
42
|
+
const PRIORITY_AT_PATH_DIRS = [
|
|
43
|
+
"src",
|
|
44
|
+
"lib",
|
|
45
|
+
"apps",
|
|
46
|
+
"packages",
|
|
47
|
+
"scripts",
|
|
48
|
+
"test",
|
|
49
|
+
"tests",
|
|
50
|
+
"docs",
|
|
51
|
+
"spec",
|
|
52
|
+
];
|
|
53
|
+
const PRIORITY_AT_PATH_FILES = [
|
|
54
|
+
"package.json",
|
|
55
|
+
"README.md",
|
|
56
|
+
"readme.md",
|
|
57
|
+
"tsconfig.json",
|
|
58
|
+
"vitest.config.ts",
|
|
59
|
+
"Cargo.toml",
|
|
60
|
+
"go.mod",
|
|
61
|
+
"pyproject.toml",
|
|
62
|
+
"Makefile",
|
|
63
|
+
];
|
|
64
|
+
function atPathSuggestionRank(label) {
|
|
65
|
+
const name = label.replace(/\/$/, "");
|
|
66
|
+
if (PRIORITY_AT_PATH_DIRS.includes(name) ||
|
|
67
|
+
PRIORITY_AT_PATH_FILES.includes(name)) {
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
if (DEPRIORITIZED_AT_PATH_NAMES.has(name)) {
|
|
71
|
+
return 3;
|
|
72
|
+
}
|
|
73
|
+
if (label.endsWith("/")) {
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
return 2;
|
|
77
|
+
}
|
|
78
|
+
function sortAtPathSuggestions(items, rawPrefix) {
|
|
79
|
+
if (rawPrefix !== "") {
|
|
80
|
+
return items.sort((a, b) => {
|
|
81
|
+
const aIsDir = a.label.endsWith("/");
|
|
82
|
+
const bIsDir = b.label.endsWith("/");
|
|
83
|
+
if (aIsDir && !bIsDir)
|
|
84
|
+
return -1;
|
|
85
|
+
if (!aIsDir && bIsDir)
|
|
86
|
+
return 1;
|
|
87
|
+
return a.label.localeCompare(b.label);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return items.sort((a, b) => {
|
|
91
|
+
const rankDiff = atPathSuggestionRank(a.label) - atPathSuggestionRank(b.label);
|
|
92
|
+
if (rankDiff !== 0)
|
|
93
|
+
return rankDiff;
|
|
94
|
+
return a.label.localeCompare(b.label);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/** `@` file attachment (not `@skill` / `@plan`). */
|
|
98
|
+
export function isAtFileAttachmentPrefix(atPrefix) {
|
|
99
|
+
if (!atPrefix)
|
|
100
|
+
return false;
|
|
101
|
+
if (parseSkillMentionPrefix(atPrefix))
|
|
102
|
+
return false;
|
|
103
|
+
if (parsePlanMentionPrefix(atPrefix))
|
|
104
|
+
return false;
|
|
105
|
+
return atPrefix.startsWith("@");
|
|
106
|
+
}
|
|
107
|
+
/** List cwd-scoped entries for `@` / `@path` (Pi getFileSuggestions semantics). */
|
|
108
|
+
export function getAtPathFileSuggestions(cwd, atPrefix) {
|
|
109
|
+
try {
|
|
110
|
+
const { rawPrefix, isAtPrefix, isQuotedPrefix } = parsePathPrefix(atPrefix);
|
|
111
|
+
if (!isAtPrefix)
|
|
112
|
+
return [];
|
|
113
|
+
let searchDir;
|
|
114
|
+
let searchPrefix;
|
|
115
|
+
const expandedPrefix = rawPrefix;
|
|
116
|
+
const isRootPrefix = rawPrefix === "" ||
|
|
117
|
+
rawPrefix === "./" ||
|
|
118
|
+
rawPrefix === "../" ||
|
|
119
|
+
rawPrefix === "/" ||
|
|
120
|
+
(isAtPrefix && rawPrefix === "");
|
|
121
|
+
if (isRootPrefix) {
|
|
122
|
+
searchDir = join(cwd, expandedPrefix);
|
|
123
|
+
searchPrefix = "";
|
|
124
|
+
}
|
|
125
|
+
else if (rawPrefix.endsWith("/")) {
|
|
126
|
+
searchDir = join(cwd, expandedPrefix);
|
|
127
|
+
searchPrefix = "";
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
searchDir = join(cwd, dirname(expandedPrefix));
|
|
131
|
+
searchPrefix = basename(expandedPrefix);
|
|
132
|
+
}
|
|
133
|
+
const entries = readdirSync(searchDir, { withFileTypes: true });
|
|
134
|
+
const suggestions = [];
|
|
135
|
+
const cwdLabel = toDisplayPath(relative(cwd, searchDir) || ".");
|
|
136
|
+
for (const entry of entries) {
|
|
137
|
+
if (!entry.name.toLowerCase().startsWith(searchPrefix.toLowerCase())) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
let isDirectory = entry.isDirectory();
|
|
141
|
+
if (!isDirectory && entry.isSymbolicLink()) {
|
|
142
|
+
try {
|
|
143
|
+
isDirectory = statSync(join(searchDir, entry.name)).isDirectory();
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// broken symlink — treat as file
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
let relativePath;
|
|
150
|
+
const displayPrefix = rawPrefix;
|
|
151
|
+
const name = entry.name;
|
|
152
|
+
if (displayPrefix.endsWith("/")) {
|
|
153
|
+
relativePath = displayPrefix + name;
|
|
154
|
+
}
|
|
155
|
+
else if (displayPrefix.includes("/")) {
|
|
156
|
+
relativePath = join(dirname(displayPrefix), name);
|
|
157
|
+
if (displayPrefix.startsWith("./") && !relativePath.startsWith("./")) {
|
|
158
|
+
relativePath = `./${relativePath}`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
relativePath = name;
|
|
163
|
+
}
|
|
164
|
+
relativePath = toDisplayPath(relativePath);
|
|
165
|
+
const pathValue = isDirectory ? `${relativePath}/` : relativePath;
|
|
166
|
+
const value = buildCompletionValue(pathValue, { isAtPrefix, isQuotedPrefix });
|
|
167
|
+
const entryRelative = toDisplayPath(relative(cwd, join(searchDir, entry.name)));
|
|
168
|
+
suggestions.push({
|
|
169
|
+
value,
|
|
170
|
+
label: name + (isDirectory ? "/" : ""),
|
|
171
|
+
description: searchDir === cwd && rawPrefix === ""
|
|
172
|
+
? entryRelative
|
|
173
|
+
: `${cwdLabel}/${entryRelative}`,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return sortAtPathSuggestions(suggestions, rawPrefix);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export function extractAtFilePrefix(textBeforeCursor) {
|
|
183
|
+
const atPrefix = extractAtPrefix(textBeforeCursor);
|
|
184
|
+
return isAtFileAttachmentPrefix(atPrefix) ? atPrefix : null;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=at-path-mentions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"at-path-mentions.js","sourceRoot":"","sources":["../../src/skills/at-path-mentions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import {
|
|
6
|
+
extractAtFilePrefix,
|
|
7
|
+
getAtPathFileSuggestions,
|
|
8
|
+
isAtFileAttachmentPrefix,
|
|
9
|
+
} from "./at-path-mentions.js";
|
|
10
|
+
|
|
11
|
+
describe("at-path-mentions", () => {
|
|
12
|
+
it("detects file @ prefix but not skill/plan", () => {
|
|
13
|
+
expect(isAtFileAttachmentPrefix("@")).toBe(true);
|
|
14
|
+
expect(isAtFileAttachmentPrefix("@src")).toBe(true);
|
|
15
|
+
expect(isAtFileAttachmentPrefix("@skill")).toBe(false);
|
|
16
|
+
expect(isAtFileAttachmentPrefix("@skill naus")).toBe(false);
|
|
17
|
+
expect(isAtFileAttachmentPrefix("@plan")).toBe(false);
|
|
18
|
+
expect(isAtFileAttachmentPrefix("@planejar")).toBe(false);
|
|
19
|
+
expect(isAtFileAttachmentPrefix("@planful")).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("extracts file @ prefix from editor text", () => {
|
|
23
|
+
expect(extractAtFilePrefix("@")).toBe("@");
|
|
24
|
+
expect(extractAtFilePrefix("read @src/")).toBe("@src/");
|
|
25
|
+
expect(extractAtFilePrefix("@skill x")).toBeNull();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("lists immediate children of session cwd for bare @", () => {
|
|
29
|
+
const cwd = mkdtempSync(join(tmpdir(), "kubbisec-at-"));
|
|
30
|
+
mkdirSync(join(cwd, "src"));
|
|
31
|
+
writeFileSync(join(cwd, "README.md"), "# hi");
|
|
32
|
+
writeFileSync(join(cwd, "package.json"), "{}");
|
|
33
|
+
|
|
34
|
+
const items = getAtPathFileSuggestions(cwd, "@");
|
|
35
|
+
const labels = items.map((item) => item.label);
|
|
36
|
+
expect(labels).toContain("src/");
|
|
37
|
+
expect(labels).toContain("README.md");
|
|
38
|
+
expect(labels).toContain("package.json");
|
|
39
|
+
expect(items.every((item) => item.value.startsWith("@"))).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("prioritizes src and package.json over node_modules for bare @", () => {
|
|
43
|
+
const cwd = mkdtempSync(join(tmpdir(), "kubbisec-at-sort-"));
|
|
44
|
+
mkdirSync(join(cwd, "node_modules"));
|
|
45
|
+
mkdirSync(join(cwd, "src"));
|
|
46
|
+
writeFileSync(join(cwd, "package.json"), "{}");
|
|
47
|
+
|
|
48
|
+
const items = getAtPathFileSuggestions(cwd, "@");
|
|
49
|
+
const labels = items.map((item) => item.label);
|
|
50
|
+
expect(labels.indexOf("src/")).toBeLessThan(labels.indexOf("node_modules/"));
|
|
51
|
+
expect(labels.indexOf("package.json")).toBeLessThan(labels.indexOf("node_modules/"));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("filters by partial name under cwd", () => {
|
|
55
|
+
const cwd = mkdtempSync(join(tmpdir(), "kubbisec-at-"));
|
|
56
|
+
writeFileSync(join(cwd, "README.md"), "# hi");
|
|
57
|
+
writeFileSync(join(cwd, "package.json"), "{}");
|
|
58
|
+
|
|
59
|
+
const items = getAtPathFileSuggestions(cwd, "@READ");
|
|
60
|
+
expect(items).toHaveLength(1);
|
|
61
|
+
expect(items[0]?.label).toBe("README.md");
|
|
62
|
+
expect(items[0]?.value).toBe("@README.md");
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, relative } from "node:path";
|
|
3
|
+
import type { AutocompleteItem } from "@earendil-works/pi-tui";
|
|
4
|
+
import {
|
|
5
|
+
extractAtPrefix,
|
|
6
|
+
parsePlanMentionPrefix,
|
|
7
|
+
parseSkillMentionPrefix,
|
|
8
|
+
} from "./chat-mentions.js";
|
|
9
|
+
|
|
10
|
+
function toDisplayPath(value: string): string {
|
|
11
|
+
return value.replace(/\\/g, "/");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function parsePathPrefix(prefix: string): {
|
|
15
|
+
rawPrefix: string;
|
|
16
|
+
isAtPrefix: boolean;
|
|
17
|
+
isQuotedPrefix: boolean;
|
|
18
|
+
} {
|
|
19
|
+
if (prefix.startsWith('@"')) {
|
|
20
|
+
return { rawPrefix: prefix.slice(2), isAtPrefix: true, isQuotedPrefix: true };
|
|
21
|
+
}
|
|
22
|
+
if (prefix.startsWith('"')) {
|
|
23
|
+
return { rawPrefix: prefix.slice(1), isAtPrefix: false, isQuotedPrefix: true };
|
|
24
|
+
}
|
|
25
|
+
if (prefix.startsWith("@")) {
|
|
26
|
+
return { rawPrefix: prefix.slice(1), isAtPrefix: true, isQuotedPrefix: false };
|
|
27
|
+
}
|
|
28
|
+
return { rawPrefix: prefix, isAtPrefix: false, isQuotedPrefix: false };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function buildCompletionValue(
|
|
32
|
+
path: string,
|
|
33
|
+
options: { isAtPrefix: boolean; isQuotedPrefix: boolean },
|
|
34
|
+
): string {
|
|
35
|
+
const needsQuotes = options.isQuotedPrefix || path.includes(" ");
|
|
36
|
+
const prefix = options.isAtPrefix ? "@" : "";
|
|
37
|
+
if (!needsQuotes) {
|
|
38
|
+
return `${prefix}${path}`;
|
|
39
|
+
}
|
|
40
|
+
const openQuote = `${prefix}"`;
|
|
41
|
+
return `${openQuote}${path}"`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const DEPRIORITIZED_AT_PATH_NAMES = new Set([
|
|
45
|
+
"node_modules",
|
|
46
|
+
"dist",
|
|
47
|
+
"dist-bin",
|
|
48
|
+
".turbo",
|
|
49
|
+
".git",
|
|
50
|
+
"coverage",
|
|
51
|
+
"build",
|
|
52
|
+
".next",
|
|
53
|
+
".cache",
|
|
54
|
+
"tmp",
|
|
55
|
+
".venv",
|
|
56
|
+
"target",
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
const PRIORITY_AT_PATH_DIRS = [
|
|
60
|
+
"src",
|
|
61
|
+
"lib",
|
|
62
|
+
"apps",
|
|
63
|
+
"packages",
|
|
64
|
+
"scripts",
|
|
65
|
+
"test",
|
|
66
|
+
"tests",
|
|
67
|
+
"docs",
|
|
68
|
+
"spec",
|
|
69
|
+
] as const;
|
|
70
|
+
|
|
71
|
+
const PRIORITY_AT_PATH_FILES = [
|
|
72
|
+
"package.json",
|
|
73
|
+
"README.md",
|
|
74
|
+
"readme.md",
|
|
75
|
+
"tsconfig.json",
|
|
76
|
+
"vitest.config.ts",
|
|
77
|
+
"Cargo.toml",
|
|
78
|
+
"go.mod",
|
|
79
|
+
"pyproject.toml",
|
|
80
|
+
"Makefile",
|
|
81
|
+
] as const;
|
|
82
|
+
|
|
83
|
+
function atPathSuggestionRank(label: string): number {
|
|
84
|
+
const name = label.replace(/\/$/, "");
|
|
85
|
+
if (
|
|
86
|
+
PRIORITY_AT_PATH_DIRS.includes(name as (typeof PRIORITY_AT_PATH_DIRS)[number]) ||
|
|
87
|
+
PRIORITY_AT_PATH_FILES.includes(name as (typeof PRIORITY_AT_PATH_FILES)[number])
|
|
88
|
+
) {
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
if (DEPRIORITIZED_AT_PATH_NAMES.has(name)) {
|
|
92
|
+
return 3;
|
|
93
|
+
}
|
|
94
|
+
if (label.endsWith("/")) {
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
return 2;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function sortAtPathSuggestions(items: AutocompleteItem[], rawPrefix: string): AutocompleteItem[] {
|
|
101
|
+
if (rawPrefix !== "") {
|
|
102
|
+
return items.sort((a, b) => {
|
|
103
|
+
const aIsDir = a.label.endsWith("/");
|
|
104
|
+
const bIsDir = b.label.endsWith("/");
|
|
105
|
+
if (aIsDir && !bIsDir) return -1;
|
|
106
|
+
if (!aIsDir && bIsDir) return 1;
|
|
107
|
+
return a.label.localeCompare(b.label);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return items.sort((a, b) => {
|
|
112
|
+
const rankDiff = atPathSuggestionRank(a.label) - atPathSuggestionRank(b.label);
|
|
113
|
+
if (rankDiff !== 0) return rankDiff;
|
|
114
|
+
return a.label.localeCompare(b.label);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** `@` file attachment (not `@skill` / `@plan`). */
|
|
119
|
+
export function isAtFileAttachmentPrefix(atPrefix: string | null): boolean {
|
|
120
|
+
if (!atPrefix) return false;
|
|
121
|
+
if (parseSkillMentionPrefix(atPrefix)) return false;
|
|
122
|
+
if (parsePlanMentionPrefix(atPrefix)) return false;
|
|
123
|
+
return atPrefix.startsWith("@");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** List cwd-scoped entries for `@` / `@path` (Pi getFileSuggestions semantics). */
|
|
127
|
+
export function getAtPathFileSuggestions(cwd: string, atPrefix: string): AutocompleteItem[] {
|
|
128
|
+
try {
|
|
129
|
+
const { rawPrefix, isAtPrefix, isQuotedPrefix } = parsePathPrefix(atPrefix);
|
|
130
|
+
if (!isAtPrefix) return [];
|
|
131
|
+
|
|
132
|
+
let searchDir: string;
|
|
133
|
+
let searchPrefix: string;
|
|
134
|
+
const expandedPrefix = rawPrefix;
|
|
135
|
+
|
|
136
|
+
const isRootPrefix =
|
|
137
|
+
rawPrefix === "" ||
|
|
138
|
+
rawPrefix === "./" ||
|
|
139
|
+
rawPrefix === "../" ||
|
|
140
|
+
rawPrefix === "/" ||
|
|
141
|
+
(isAtPrefix && rawPrefix === "");
|
|
142
|
+
|
|
143
|
+
if (isRootPrefix) {
|
|
144
|
+
searchDir = join(cwd, expandedPrefix);
|
|
145
|
+
searchPrefix = "";
|
|
146
|
+
} else if (rawPrefix.endsWith("/")) {
|
|
147
|
+
searchDir = join(cwd, expandedPrefix);
|
|
148
|
+
searchPrefix = "";
|
|
149
|
+
} else {
|
|
150
|
+
searchDir = join(cwd, dirname(expandedPrefix));
|
|
151
|
+
searchPrefix = basename(expandedPrefix);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const entries = readdirSync(searchDir, { withFileTypes: true });
|
|
155
|
+
const suggestions: AutocompleteItem[] = [];
|
|
156
|
+
const cwdLabel = toDisplayPath(relative(cwd, searchDir) || ".");
|
|
157
|
+
|
|
158
|
+
for (const entry of entries) {
|
|
159
|
+
if (!entry.name.toLowerCase().startsWith(searchPrefix.toLowerCase())) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let isDirectory = entry.isDirectory();
|
|
164
|
+
if (!isDirectory && entry.isSymbolicLink()) {
|
|
165
|
+
try {
|
|
166
|
+
isDirectory = statSync(join(searchDir, entry.name)).isDirectory();
|
|
167
|
+
} catch {
|
|
168
|
+
// broken symlink — treat as file
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let relativePath: string;
|
|
173
|
+
const displayPrefix = rawPrefix;
|
|
174
|
+
const name = entry.name;
|
|
175
|
+
|
|
176
|
+
if (displayPrefix.endsWith("/")) {
|
|
177
|
+
relativePath = displayPrefix + name;
|
|
178
|
+
} else if (displayPrefix.includes("/")) {
|
|
179
|
+
relativePath = join(dirname(displayPrefix), name);
|
|
180
|
+
if (displayPrefix.startsWith("./") && !relativePath.startsWith("./")) {
|
|
181
|
+
relativePath = `./${relativePath}`;
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
relativePath = name;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
relativePath = toDisplayPath(relativePath);
|
|
188
|
+
const pathValue = isDirectory ? `${relativePath}/` : relativePath;
|
|
189
|
+
const value = buildCompletionValue(pathValue, { isAtPrefix, isQuotedPrefix });
|
|
190
|
+
const entryRelative = toDisplayPath(relative(cwd, join(searchDir, entry.name)));
|
|
191
|
+
|
|
192
|
+
suggestions.push({
|
|
193
|
+
value,
|
|
194
|
+
label: name + (isDirectory ? "/" : ""),
|
|
195
|
+
description:
|
|
196
|
+
searchDir === cwd && rawPrefix === ""
|
|
197
|
+
? entryRelative
|
|
198
|
+
: `${cwdLabel}/${entryRelative}`,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return sortAtPathSuggestions(suggestions, rawPrefix);
|
|
203
|
+
} catch {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function extractAtFilePrefix(textBeforeCursor: string): string | null {
|
|
209
|
+
const atPrefix = extractAtPrefix(textBeforeCursor);
|
|
210
|
+
return isAtFileAttachmentPrefix(atPrefix) ? atPrefix : null;
|
|
211
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-mention-render.d.ts","sourceRoot":"","sources":["../../src/skills/chat-mention-render.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chat-mention-render.d.ts","sourceRoot":"","sources":["../../src/skills/chat-mention-render.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC5C,CAAC;AA0IF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAIxF;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAqB5E;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAqB5E"}
|