@noclaf/cli 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -0
- package/dist/auth.js +66 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli/commands/auth.js +76 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/init.js +291 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/skills.js +22 -0
- package/dist/cli/commands/skills.js.map +1 -0
- package/dist/cli/commands/sync.js +170 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/copy/auth.json +6 -0
- package/dist/cli/copy/flows.json +6 -0
- package/dist/cli/copy/init.json +19 -0
- package/dist/cli/copy/sync.json +3 -0
- package/dist/cli/copy.js +23 -0
- package/dist/cli/copy.js.map +1 -0
- package/dist/cli/flows/clients.js +108 -0
- package/dist/cli/flows/clients.js.map +1 -0
- package/dist/cli/flows/dependencies.js +68 -0
- package/dist/cli/flows/dependencies.js.map +1 -0
- package/dist/cli/flows/sections.js +45 -0
- package/dist/cli/flows/sections.js.map +1 -0
- package/dist/cli/ui/render.js +167 -0
- package/dist/cli/ui/render.js.map +1 -0
- package/dist/cli.js +25 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +96 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.js +10 -0
- package/dist/constants.js.map +1 -0
- package/dist/database.types.js +11 -0
- package/dist/database.types.js.map +1 -0
- package/dist/lib/active-project.js +28 -0
- package/dist/lib/active-project.js.map +1 -0
- package/dist/lib/client-configs.js +179 -0
- package/dist/lib/client-configs.js.map +1 -0
- package/dist/lib/client-install.js +40 -0
- package/dist/lib/client-install.js.map +1 -0
- package/dist/lib/colors.js +67 -0
- package/dist/lib/colors.js.map +1 -0
- package/dist/lib/cowork-extension.js +131 -0
- package/dist/lib/cowork-extension.js.map +1 -0
- package/dist/lib/dependencies.js +159 -0
- package/dist/lib/dependencies.js.map +1 -0
- package/dist/lib/duration.js +5 -0
- package/dist/lib/duration.js.map +1 -0
- package/dist/lib/file-merge.js +33 -0
- package/dist/lib/file-merge.js.map +1 -0
- package/dist/lib/hooks.js +231 -0
- package/dist/lib/hooks.js.map +1 -0
- package/dist/lib/project-context.js +105 -0
- package/dist/lib/project-context.js.map +1 -0
- package/dist/lib/prompts.js +62 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/lib/provision.js +222 -0
- package/dist/lib/provision.js.map +1 -0
- package/dist/lib/require-config.js +31 -0
- package/dist/lib/require-config.js.map +1 -0
- package/dist/lib/sections.js +85 -0
- package/dist/lib/sections.js.map +1 -0
- package/dist/lib/skill-serve.js +71 -0
- package/dist/lib/skill-serve.js.map +1 -0
- package/dist/lib/skills-cache.js +89 -0
- package/dist/lib/skills-cache.js.map +1 -0
- package/dist/lib/skills.js +219 -0
- package/dist/lib/skills.js.map +1 -0
- package/dist/lib/targets.js +150 -0
- package/dist/lib/targets.js.map +1 -0
- package/dist/lib/task-history.js +14 -0
- package/dist/lib/task-history.js.map +1 -0
- package/dist/lib/telemetry.js +32 -0
- package/dist/lib/telemetry.js.map +1 -0
- package/dist/lib/tool-result.js +12 -0
- package/dist/lib/tool-result.js.map +1 -0
- package/dist/lib/version-check.js +88 -0
- package/dist/lib/version-check.js.map +1 -0
- package/dist/logo.js +17 -0
- package/dist/logo.js.map +1 -0
- package/dist/mcp-server.js +162 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/spinner.js +12 -0
- package/dist/spinner.js.map +1 -0
- package/dist/supabase.js +95 -0
- package/dist/supabase.js.map +1 -0
- package/dist/tools/comment-task.js +80 -0
- package/dist/tools/comment-task.js.map +1 -0
- package/dist/tools/create-task.js +194 -0
- package/dist/tools/create-task.js.map +1 -0
- package/dist/tools/end-allocation.js +59 -0
- package/dist/tools/end-allocation.js.map +1 -0
- package/dist/tools/end-task-allocation.js +51 -0
- package/dist/tools/end-task-allocation.js.map +1 -0
- package/dist/tools/get-active-allocation.js +106 -0
- package/dist/tools/get-active-allocation.js.map +1 -0
- package/dist/tools/get-context.js +47 -0
- package/dist/tools/get-context.js.map +1 -0
- package/dist/tools/get-task.js +223 -0
- package/dist/tools/get-task.js.map +1 -0
- package/dist/tools/index.js +34 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-my-allocations.js +87 -0
- package/dist/tools/list-my-allocations.js.map +1 -0
- package/dist/tools/list-projects.js +34 -0
- package/dist/tools/list-projects.js.map +1 -0
- package/dist/tools/list-tasks.js +213 -0
- package/dist/tools/list-tasks.js.map +1 -0
- package/dist/tools/move-task.js +98 -0
- package/dist/tools/move-task.js.map +1 -0
- package/dist/tools/set-project.js +47 -0
- package/dist/tools/set-project.js.map +1 -0
- package/dist/tools/start-allocation.js +45 -0
- package/dist/tools/start-allocation.js.map +1 -0
- package/dist/tools/start-task-allocation.js +141 -0
- package/dist/tools/start-task-allocation.js.map +1 -0
- package/dist/tools/update-task.js +256 -0
- package/dist/tools/update-task.js.map +1 -0
- package/dist/version.js +16 -0
- package/dist/version.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import boxen from 'boxen';
|
|
3
|
+
/**
|
|
4
|
+
* Camada de estilo do CLI. `chalk` cuida de cor (respeita TTY / `NO_COLOR` /
|
|
5
|
+
* `FORCE_COLOR` sozinho) e `boxen` desenha caixas. Helpers extras: links
|
|
6
|
+
* clicáveis (OSC 8), régua, símbolos e realce de comandos.
|
|
7
|
+
*/
|
|
8
|
+
/** Paleta — `chalk` já é um objeto com `.bold/.dim/.cyan/...`, reexpomos como `c`. */
|
|
9
|
+
export const c = chalk;
|
|
10
|
+
/** Símbolos usados na saída (unicode). */
|
|
11
|
+
export const sym = {
|
|
12
|
+
ok: '✓',
|
|
13
|
+
err: '✗',
|
|
14
|
+
dot: '●',
|
|
15
|
+
arrow: '→',
|
|
16
|
+
chevron: '›',
|
|
17
|
+
bullet: '·',
|
|
18
|
+
warn: '⚠',
|
|
19
|
+
gear: '↻',
|
|
20
|
+
};
|
|
21
|
+
/** Comando destacado (ciano) — o realce padrão pra qualquer coisa executável. */
|
|
22
|
+
export function cmd(s) {
|
|
23
|
+
return chalk.cyan(s);
|
|
24
|
+
}
|
|
25
|
+
/** Régua horizontal fina. */
|
|
26
|
+
export function rule(width = 58) {
|
|
27
|
+
return chalk.dim('─'.repeat(width));
|
|
28
|
+
}
|
|
29
|
+
/** Realça trechos `entre crases` como comandos (ciano) e remove as crases. */
|
|
30
|
+
export function highlightInlineCode(s) {
|
|
31
|
+
return s.replace(/`([^`]+)`/g, (_m, code) => chalk.cyan(code));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Link clicável via OSC 8 (terminais modernos). Fora de TTY, cai pra
|
|
35
|
+
* `label (url)` em texto plano. Sublinhado em ciano pra parecer link.
|
|
36
|
+
*/
|
|
37
|
+
export function link(url, label = url) {
|
|
38
|
+
if (!process.stdout.isTTY)
|
|
39
|
+
return label === url ? url : `${label} (${url})`;
|
|
40
|
+
const OSC = ']8;;';
|
|
41
|
+
const BEL = '';
|
|
42
|
+
return `${OSC}${url}${BEL}${chalk.cyan.underline(label)}${OSC}${BEL}`;
|
|
43
|
+
}
|
|
44
|
+
/** Caixa (boxen) com defaults do noclaf; opções sobrescrevíveis. */
|
|
45
|
+
export function box(content, opts = {}) {
|
|
46
|
+
return boxen(content, {
|
|
47
|
+
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
48
|
+
borderStyle: 'round',
|
|
49
|
+
borderColor: 'gray',
|
|
50
|
+
dimBorder: true,
|
|
51
|
+
...opts,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** Indenta cada linha de um bloco multi-linha (ex.: pra alinhar uma caixa). */
|
|
55
|
+
export function indent(block, spaces = 2) {
|
|
56
|
+
const pad = ' '.repeat(spaces);
|
|
57
|
+
return block
|
|
58
|
+
.split('\n')
|
|
59
|
+
.map((l) => (l.length ? pad + l : l))
|
|
60
|
+
.join('\n');
|
|
61
|
+
}
|
|
62
|
+
/** Título de seção proeminente: negrito + sublinhado, com subtítulo dim opcional. */
|
|
63
|
+
export function sectionTitle(title, subtitle) {
|
|
64
|
+
const head = chalk.bold.underline(title);
|
|
65
|
+
return subtitle ? `${head} ${chalk.dim(subtitle)}` : head;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=colors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/lib/colors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAuC,MAAM,OAAO,CAAC;AAE5D;;;;GAIG;AAEH,sFAAsF;AACtF,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;AAEvB,0CAA0C;AAC1C,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,EAAE,EAAE,GAAG;IACP,GAAG,EAAE,GAAG;IACR,GAAG,EAAE,GAAG;IACR,KAAK,EAAE,GAAG;IACV,OAAO,EAAE,GAAG;IACZ,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;CACV,CAAC;AAEF,iFAAiF;AACjF,MAAM,UAAU,GAAG,CAAC,CAAS;IAC3B,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,IAAI,CAAC,KAAK,GAAG,EAAE;IAC7B,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACtC,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,mBAAmB,CAAC,CAAS;IAC3C,OAAO,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,IAAI,CAAC,GAAW,EAAE,KAAK,GAAG,GAAG;IAC3C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK;QAAE,OAAO,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,GAAG,GAAG,CAAC;IAC5E,MAAM,GAAG,GAAG,OAAO,CAAC;IACpB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,OAAO,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC;AACxE,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,OAAqB,EAAE;IAC1D,OAAO,KAAK,CAAC,OAAO,EAAE;QACpB,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;QACjD,WAAW,EAAE,OAAO;QACpB,WAAW,EAAE,MAAM;QACnB,SAAS,EAAE,IAAI;QACf,GAAG,IAAI;KACR,CAAC,CAAC;AACL,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,MAAM,GAAG,CAAC;IAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,OAAO,KAAK;SACT,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACpC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,QAAiB;IAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import AdmZip from "adm-zip";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { VERSION } from "../version.js";
|
|
8
|
+
import { toolDefinitions } from "../tools/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Raiz do pacote instalado (onde ficam `dist/` e `node_modules/`).
|
|
11
|
+
* Este módulo compila para dist/lib/cowork-extension.js, então sobe dois níveis.
|
|
12
|
+
*/
|
|
13
|
+
function packageRoot() {
|
|
14
|
+
const here = dirname(fileURLToPath(import.meta.url)); // .../dist/lib
|
|
15
|
+
return join(here, "..", "..");
|
|
16
|
+
}
|
|
17
|
+
/** Gera o objeto manifest.json (spec MCPB) para a versão atual. */
|
|
18
|
+
export function buildCoworkManifest() {
|
|
19
|
+
const tools = toolDefinitions.map((t) => ({
|
|
20
|
+
name: t.name,
|
|
21
|
+
description: (t.description ?? "")
|
|
22
|
+
.replace(/\s+/g, " ")
|
|
23
|
+
.trim()
|
|
24
|
+
.slice(0, 160),
|
|
25
|
+
}));
|
|
26
|
+
return {
|
|
27
|
+
manifest_version: "0.2",
|
|
28
|
+
name: "noclaf-cli",
|
|
29
|
+
display_name: "Noclaf (NOS)",
|
|
30
|
+
version: VERSION,
|
|
31
|
+
description: "MCP server pra controlar o NOS (Noclaf Operation System) — tarefas, kanban e ponto/cronometragem — a partir do Claude Desktop / Cowork.",
|
|
32
|
+
long_description: "Servidor MCP do NOS (Noclaf Operation System). Permite ao assistente ler o contexto do projeto, listar/criar/atualizar/mover/comentar tarefas e gerenciar alocações (bater ponto e cronometrar tempo por task). A autenticação usa um PAT do NOS, fornecido via configuração da extensão (NOCLAF_PAT).",
|
|
33
|
+
author: { name: "Noclaf Tech", url: "https://github.com/Falcao-Tech" },
|
|
34
|
+
homepage: "https://github.com/Falcao-Tech/noclaf-cli#readme",
|
|
35
|
+
documentation: "https://github.com/Falcao-Tech/noclaf-cli#readme",
|
|
36
|
+
support: "https://github.com/Falcao-Tech/noclaf-cli/issues",
|
|
37
|
+
keywords: ["mcp", "noclaf", "nos", "tasks", "kanban", "time-tracking"],
|
|
38
|
+
server: {
|
|
39
|
+
type: "node",
|
|
40
|
+
entry_point: "dist/mcp-server.js",
|
|
41
|
+
mcp_config: {
|
|
42
|
+
command: "node",
|
|
43
|
+
args: ["${__dirname}/dist/mcp-server.js"],
|
|
44
|
+
env: {
|
|
45
|
+
NOCLAF_PAT: '${user_config.pat}',
|
|
46
|
+
NOCLAF_PROJECT_ID: '${user_config.project_id}',
|
|
47
|
+
NOCLAF_REPOSITORY_ID: '${user_config.repository_id}',
|
|
48
|
+
// Cowork ignora ~/.claude → o servidor pula o auto-pull e entrega as skills
|
|
49
|
+
// por resources + prompts.
|
|
50
|
+
NOCLAF_CLIENT: 'cowork',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
user_config: {
|
|
55
|
+
pat: {
|
|
56
|
+
type: "string",
|
|
57
|
+
title: "NOS Personal Access Token (PAT)",
|
|
58
|
+
description: "PAT do NOS (formato noc_…), gerado em https://nos.noclaf.com.br/profile#mcp. O projeto é escolhido por sessão com as tools nos_list_projects / nos_set_project — não há nada de projeto para configurar aqui.",
|
|
59
|
+
sensitive: true,
|
|
60
|
+
required: true,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
tools,
|
|
64
|
+
compatibility: {
|
|
65
|
+
platforms: ["darwin", "win32", "linux"],
|
|
66
|
+
runtimes: { node: ">=20.0.0" },
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Adiciona recursivamente os arquivos de `localDir` ao zip sob `zipPrefix`,
|
|
72
|
+
* usando separadores POSIX e sem criar entradas de diretório. Pula source maps.
|
|
73
|
+
*/
|
|
74
|
+
function addFilesRecursively(zip, localDir, zipPrefix) {
|
|
75
|
+
for (const entry of readdirSync(localDir, { withFileTypes: true })) {
|
|
76
|
+
const localPath = join(localDir, entry.name);
|
|
77
|
+
const zipPath = `${zipPrefix}/${entry.name}`;
|
|
78
|
+
if (entry.isDirectory()) {
|
|
79
|
+
addFilesRecursively(zip, localPath, zipPath);
|
|
80
|
+
}
|
|
81
|
+
else if (entry.isFile() && !entry.name.endsWith(".map")) {
|
|
82
|
+
zip.addFile(zipPath, readFileSync(localPath));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Pasta de destino do .mcpb: ~/Downloads se existir, senão o home. */
|
|
87
|
+
function defaultOutputDir() {
|
|
88
|
+
const downloads = join(homedir(), "Downloads");
|
|
89
|
+
return existsSync(downloads) ? downloads : homedir();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Gera o pacote .mcpb (manifest + dist + node_modules) a partir da instalação atual.
|
|
93
|
+
* Source maps são omitidos pra reduzir o tamanho.
|
|
94
|
+
*/
|
|
95
|
+
export function generateCoworkExtension(outputDir = defaultOutputDir()) {
|
|
96
|
+
const root = packageRoot();
|
|
97
|
+
const distDir = join(root, "dist");
|
|
98
|
+
const nodeModulesDir = join(root, "node_modules");
|
|
99
|
+
if (!existsSync(distDir)) {
|
|
100
|
+
throw new Error(`Não encontrei dist/ em ${root}. Reinstale o pacote (npm i -g @noclaf/cli).`);
|
|
101
|
+
}
|
|
102
|
+
const manifest = buildCoworkManifest();
|
|
103
|
+
const zip = new AdmZip();
|
|
104
|
+
zip.addFile("manifest.json", Buffer.from(JSON.stringify(manifest, null, 2) + "\n", "utf8"));
|
|
105
|
+
// Adiciona só arquivos (sem entradas de diretório), como o `mcpb pack` faz —
|
|
106
|
+
// entradas de pasta quebram o instalador do MCPB. Source maps são omitidos.
|
|
107
|
+
addFilesRecursively(zip, distDir, "dist");
|
|
108
|
+
if (existsSync(nodeModulesDir)) {
|
|
109
|
+
addFilesRecursively(zip, nodeModulesDir, "node_modules");
|
|
110
|
+
}
|
|
111
|
+
const outputPath = join(outputDir, `noclaf-cli-${VERSION}.mcpb`);
|
|
112
|
+
zip.writeZip(outputPath);
|
|
113
|
+
return { outputPath };
|
|
114
|
+
}
|
|
115
|
+
/** Abre a pasta no explorador de arquivos do SO (best-effort, ignora falhas). */
|
|
116
|
+
export function revealInFileManager(target) {
|
|
117
|
+
const cmd = process.platform === "darwin"
|
|
118
|
+
? "open"
|
|
119
|
+
: process.platform === "win32"
|
|
120
|
+
? "explorer"
|
|
121
|
+
: "xdg-open";
|
|
122
|
+
try {
|
|
123
|
+
const child = spawn(cmd, [target], { stdio: "ignore", detached: true });
|
|
124
|
+
child.on("error", () => { });
|
|
125
|
+
child.unref();
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// sem file manager disponível — ignora
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=cowork-extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cowork-extension.js","sourceRoot":"","sources":["../../src/lib/cowork-extension.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
/**
|
|
6
|
+
* Instalador de dependências externas declaradas no pacote (docs `dependency`).
|
|
7
|
+
*
|
|
8
|
+
* **Segurança.** NÃO executamos a string `install:` (ela é só pra exibição). Em vez
|
|
9
|
+
* disso lemos campos **estruturados e allowlisted** (`npm:` / `skills:` / `git:`),
|
|
10
|
+
* validamos com regex estrita e montamos o comando nós mesmos — rodado via
|
|
11
|
+
* `spawnSync` com array de args e SEM shell, então não há interpolação/injeção.
|
|
12
|
+
*
|
|
13
|
+
* A decisão de *rodar* fica na CLI, com consentimento humano [y/N].
|
|
14
|
+
*/
|
|
15
|
+
/** Nome de pacote npm (com escopo opcional). Sem espaços/metacaracteres de shell. */
|
|
16
|
+
const NPM_NAME_RE = /^(?:@[a-z0-9-][a-z0-9-._]*\/)?[a-z0-9-][a-z0-9-._]*$/i;
|
|
17
|
+
/** Só clones https do GitHub. */
|
|
18
|
+
const GIT_URL_RE = /^https:\/\/github\.com\/[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+(?:\.git)?$/;
|
|
19
|
+
/** Slug `owner/repo` pro `npx skills add`. */
|
|
20
|
+
const SKILLS_REPO_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
21
|
+
/** Onde clonamos deps do tipo `git` (`~/.noclaf/deps/<id>`). */
|
|
22
|
+
function depsDir() {
|
|
23
|
+
return join(homedir(), '.noclaf', 'deps');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve um doc `dependency` num plano de instalação a partir de campos
|
|
27
|
+
* estruturados. Precedência: `npm:` > `skills:` > `git:`. Sem campo válido → `plan: null`.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveDependency(node) {
|
|
30
|
+
const fm = node.frontmatter;
|
|
31
|
+
const base = {
|
|
32
|
+
id: node.id,
|
|
33
|
+
title: node.title,
|
|
34
|
+
description: node.description || fm.description || undefined,
|
|
35
|
+
repo: fm.repo || fm.homepage || undefined,
|
|
36
|
+
displayInstall: fm.install || undefined,
|
|
37
|
+
plan: null,
|
|
38
|
+
};
|
|
39
|
+
const npm = fm.npm?.trim();
|
|
40
|
+
if (npm) {
|
|
41
|
+
if (NPM_NAME_RE.test(npm)) {
|
|
42
|
+
return {
|
|
43
|
+
...base,
|
|
44
|
+
plan: {
|
|
45
|
+
kind: 'npm',
|
|
46
|
+
pkg: npm,
|
|
47
|
+
program: 'npm',
|
|
48
|
+
args: ['install', '-g', npm],
|
|
49
|
+
command: `npm install -g ${npm}`,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return { ...base, reason: `nome de pacote npm inválido: "${npm}"` };
|
|
54
|
+
}
|
|
55
|
+
const skills = fm.skills?.trim();
|
|
56
|
+
if (skills) {
|
|
57
|
+
if (SKILLS_REPO_RE.test(skills)) {
|
|
58
|
+
return {
|
|
59
|
+
...base,
|
|
60
|
+
plan: {
|
|
61
|
+
kind: 'skills',
|
|
62
|
+
repo: skills,
|
|
63
|
+
program: 'npx',
|
|
64
|
+
args: ['--yes', 'skills', 'add', skills],
|
|
65
|
+
command: `npx --yes skills add ${skills}`,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return { ...base, reason: `repo de skills inválido (owner/repo): "${skills}"` };
|
|
70
|
+
}
|
|
71
|
+
const git = fm.git?.trim();
|
|
72
|
+
if (git) {
|
|
73
|
+
if (GIT_URL_RE.test(git)) {
|
|
74
|
+
const dest = join(depsDir(), node.id);
|
|
75
|
+
return {
|
|
76
|
+
...base,
|
|
77
|
+
plan: {
|
|
78
|
+
kind: 'git',
|
|
79
|
+
url: git,
|
|
80
|
+
dest,
|
|
81
|
+
program: 'git',
|
|
82
|
+
args: ['clone', '--depth', '1', git, dest],
|
|
83
|
+
command: `git clone --depth 1 ${git} ${dest}`,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return { ...base, reason: `URL git não permitida (só https://github.com/…): "${git}"` };
|
|
88
|
+
}
|
|
89
|
+
// Sem instalador automatizável: cai pra instrução manual (plugin/marketplace/UI).
|
|
90
|
+
const manual = fm.manual?.trim();
|
|
91
|
+
if (manual)
|
|
92
|
+
return { ...base, manual, reason: 'instalação manual' };
|
|
93
|
+
return { ...base, reason: 'sem instalador estruturado (npm:/skills:/git:/manual:) — instale manualmente' };
|
|
94
|
+
}
|
|
95
|
+
/** Registro do que o noclaf instalou: `~/.noclaf/installed-deps.json`. */
|
|
96
|
+
function markerPath() {
|
|
97
|
+
return join(homedir(), '.noclaf', 'installed-deps.json');
|
|
98
|
+
}
|
|
99
|
+
function readMarker() {
|
|
100
|
+
try {
|
|
101
|
+
return JSON.parse(readFileSync(markerPath(), 'utf-8'));
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return {};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** Grava que instalamos esta dependência (idempotência p/ `skills`, que não tem local padrão). */
|
|
108
|
+
export function recordDependencyInstalled(dep) {
|
|
109
|
+
if (!dep.plan)
|
|
110
|
+
return;
|
|
111
|
+
const ref = dep.plan.kind === 'npm' ? dep.plan.pkg : dep.plan.kind === 'skills' ? dep.plan.repo : dep.plan.url;
|
|
112
|
+
const marker = readMarker();
|
|
113
|
+
marker[dep.id] = { kind: dep.plan.kind, ref, at: new Date().toISOString() };
|
|
114
|
+
try {
|
|
115
|
+
mkdirSync(dirname(markerPath()), { recursive: true });
|
|
116
|
+
writeFileSync(markerPath(), JSON.stringify(marker, null, 2) + '\n');
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// best-effort — não bloqueia se ~/.noclaf não for gravável.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** Probe de locais conhecidos onde `npx skills add` costuma instalar. */
|
|
123
|
+
function skillInstalledElsewhere(repo) {
|
|
124
|
+
const name = repo.split('/').pop() ?? repo;
|
|
125
|
+
return [
|
|
126
|
+
join(homedir(), '.claude', 'skills', name),
|
|
127
|
+
join(homedir(), '.codex', 'skills', name),
|
|
128
|
+
join(process.cwd(), '.claude', 'skills', name),
|
|
129
|
+
join(process.cwd(), 'skills', name),
|
|
130
|
+
].some((p) => existsSync(p));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Já está instalado? Checagem real quando dá:
|
|
134
|
+
* - `npm` → `npm ls -g <pkg>` (autoritativo);
|
|
135
|
+
* - `git` → diretório de destino existe;
|
|
136
|
+
* - `skills` → sem local padrão: usa o marcador do noclaf + probe de dirs conhecidos.
|
|
137
|
+
*/
|
|
138
|
+
export function isDependencyInstalled(dep) {
|
|
139
|
+
const plan = dep.plan;
|
|
140
|
+
if (!plan)
|
|
141
|
+
return false;
|
|
142
|
+
if (plan.kind === 'git')
|
|
143
|
+
return existsSync(plan.dest);
|
|
144
|
+
if (plan.kind === 'npm') {
|
|
145
|
+
const res = spawnSync('npm', ['ls', '-g', plan.pkg, '--depth=0'], { stdio: 'ignore' });
|
|
146
|
+
return res.status === 0;
|
|
147
|
+
}
|
|
148
|
+
return Boolean(readMarker()[dep.id]) || skillInstalledElsewhere(plan.repo);
|
|
149
|
+
}
|
|
150
|
+
/** Executa o plano. `spawnSync` com args em array e SEM shell — zero injeção. */
|
|
151
|
+
export function runDependencyInstall(plan) {
|
|
152
|
+
if (plan.kind === 'git')
|
|
153
|
+
mkdirSync(dirname(plan.dest), { recursive: true });
|
|
154
|
+
const res = spawnSync(plan.program, plan.args, { stdio: 'inherit' });
|
|
155
|
+
if (res.error)
|
|
156
|
+
return { ok: false, code: null, error: res.error.message };
|
|
157
|
+
return { ok: res.status === 0, code: res.status };
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=dependencies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencies.js","sourceRoot":"","sources":["../../src/lib/dependencies.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.js","sourceRoot":"","sources":["../../src/lib/duration.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,GAAkB;IAC/D,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACzD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync, } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { parse as parseToml, stringify as stringifyToml } from 'smol-toml';
|
|
4
|
+
export function backupFile(path) {
|
|
5
|
+
const backupPath = `${path}.bak.${Date.now()}`;
|
|
6
|
+
copyFileSync(path, backupPath);
|
|
7
|
+
return backupPath;
|
|
8
|
+
}
|
|
9
|
+
export function readJson(path) {
|
|
10
|
+
if (!existsSync(path))
|
|
11
|
+
return null;
|
|
12
|
+
const raw = readFileSync(path, 'utf-8');
|
|
13
|
+
if (raw.trim().length === 0)
|
|
14
|
+
return null;
|
|
15
|
+
return JSON.parse(raw);
|
|
16
|
+
}
|
|
17
|
+
export function writeJson(path, data) {
|
|
18
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
19
|
+
writeFileSync(path, JSON.stringify(data, null, 2) + '\n', 'utf-8');
|
|
20
|
+
}
|
|
21
|
+
export function readToml(path) {
|
|
22
|
+
if (!existsSync(path))
|
|
23
|
+
return null;
|
|
24
|
+
const raw = readFileSync(path, 'utf-8');
|
|
25
|
+
if (raw.trim().length === 0)
|
|
26
|
+
return null;
|
|
27
|
+
return parseToml(raw);
|
|
28
|
+
}
|
|
29
|
+
export function writeToml(path, data) {
|
|
30
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
31
|
+
writeFileSync(path, stringifyToml(data) + '\n', 'utf-8');
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=file-merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-merge.js","sourceRoot":"","sources":["../../src/lib/file-merge.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,UAAU,EACV,SAAS,EACT,YAAY,GACb,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,WAAW,CAAC;AAE3E,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,UAAU,GAAG,GAAG,IAAI,QAAQ,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAC/C,YAAY,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC/B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAa;IACnD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO,SAAS,CAAC,GAAG,CAA4B,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAA6B;IACnE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, mkdirSync, copyFileSync, rmSync, } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { readJson, writeJson } from './file-merge.js';
|
|
5
|
+
import { skillsDir } from './skills.js';
|
|
6
|
+
import { discoverRoles, resolveRoles, } from './sections.js';
|
|
7
|
+
/**
|
|
8
|
+
* Provisionamento de hooks (só Claude Code). Um hook é um script + um gatilho que
|
|
9
|
+
* precisa estar registrado no `settings.json` — então, além de copiar o script pro
|
|
10
|
+
* namespace `hooks/noclaf/`, o CLI faz merge não-destrutivo do binding. Fonte:
|
|
11
|
+
* `@noclaf/skills/hooks/<role>/hooks.json`.
|
|
12
|
+
*/
|
|
13
|
+
/** Namespace sob o targetDir onde os scripts do noclaf vivem. Marca "nossa" entrada. */
|
|
14
|
+
const HOOKS_NS = 'hooks/noclaf';
|
|
15
|
+
function defaultTargetDir() {
|
|
16
|
+
return join(homedir(), '.claude');
|
|
17
|
+
}
|
|
18
|
+
/** Lê e valida um `hooks.json`, devolvendo as entradas bem-formadas. */
|
|
19
|
+
function readManifest(path) {
|
|
20
|
+
let parsed;
|
|
21
|
+
try {
|
|
22
|
+
parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
const list = parsed?.hooks;
|
|
28
|
+
if (!Array.isArray(list))
|
|
29
|
+
return [];
|
|
30
|
+
const out = [];
|
|
31
|
+
for (const raw of list) {
|
|
32
|
+
if (!raw || typeof raw !== 'object')
|
|
33
|
+
continue;
|
|
34
|
+
const e = raw;
|
|
35
|
+
if (typeof e.event !== 'string' || typeof e.script !== 'string')
|
|
36
|
+
continue;
|
|
37
|
+
out.push({
|
|
38
|
+
id: typeof e.id === 'number' ? e.id : undefined,
|
|
39
|
+
event: e.event,
|
|
40
|
+
matcher: typeof e.matcher === 'string' ? e.matcher : undefined,
|
|
41
|
+
description: typeof e.description === 'string' ? e.description : undefined,
|
|
42
|
+
script: e.script,
|
|
43
|
+
clients: Array.isArray(e.clients)
|
|
44
|
+
? e.clients.filter((c) => typeof c === 'string')
|
|
45
|
+
: undefined,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Descobre e resolve os hooks aplicáveis: varre `hooks/<role>/hooks.json`, filtra por
|
|
52
|
+
* seção (role) e por cliente (`clients`), e devolve as entradas com o script de origem.
|
|
53
|
+
*/
|
|
54
|
+
function collectHooks(dir, surface, sections) {
|
|
55
|
+
const hooksRoot = join(dir, 'hooks');
|
|
56
|
+
if (!existsSync(hooksRoot))
|
|
57
|
+
return [];
|
|
58
|
+
// Roles permitidos pela seleção (perfil). Sem seleção → todos.
|
|
59
|
+
const allRoles = new Set(discoverRoles(dir));
|
|
60
|
+
for (const e of readdirSync(hooksRoot, { withFileTypes: true })) {
|
|
61
|
+
if (e.isDirectory() && !e.name.startsWith('.'))
|
|
62
|
+
allRoles.add(e.name);
|
|
63
|
+
}
|
|
64
|
+
const allowedRoles = sections
|
|
65
|
+
? resolveRoles(sections, [...allRoles])
|
|
66
|
+
: null; // null = sem filtro
|
|
67
|
+
const out = [];
|
|
68
|
+
for (const roleEntry of readdirSync(hooksRoot, { withFileTypes: true })) {
|
|
69
|
+
if (!roleEntry.isDirectory() || roleEntry.name.startsWith('.'))
|
|
70
|
+
continue;
|
|
71
|
+
const role = roleEntry.name;
|
|
72
|
+
if (allowedRoles && !allowedRoles.has(role))
|
|
73
|
+
continue;
|
|
74
|
+
const manifestPath = join(hooksRoot, role, 'hooks.json');
|
|
75
|
+
if (!existsSync(manifestPath))
|
|
76
|
+
continue;
|
|
77
|
+
for (const entry of readManifest(manifestPath)) {
|
|
78
|
+
if (entry.clients && !entry.clients.includes(surface))
|
|
79
|
+
continue;
|
|
80
|
+
const srcAbs = join(hooksRoot, role, entry.script);
|
|
81
|
+
if (!existsSync(srcAbs))
|
|
82
|
+
continue;
|
|
83
|
+
out.push({
|
|
84
|
+
role,
|
|
85
|
+
id: entry.id ?? null,
|
|
86
|
+
event: entry.event,
|
|
87
|
+
matcher: entry.matcher,
|
|
88
|
+
description: entry.description,
|
|
89
|
+
script: entry.script,
|
|
90
|
+
srcAbs,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
/** Uma entrada de comando é "nossa" se aponta pro namespace `hooks/noclaf/`. */
|
|
97
|
+
function isOurs(hook) {
|
|
98
|
+
const cmd = hook?.command;
|
|
99
|
+
return typeof cmd === 'string' && cmd.includes(`${HOOKS_NS}/`);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Remove todas as entradas do noclaf de um bloco de `hooks` do settings, deixando
|
|
103
|
+
* intactas as do usuário. Devolve o bloco limpo (eventos/grupos vazios removidos).
|
|
104
|
+
*/
|
|
105
|
+
function stripOurs(hooksCfg) {
|
|
106
|
+
const clean = {};
|
|
107
|
+
for (const [event, groups] of Object.entries(hooksCfg)) {
|
|
108
|
+
if (!Array.isArray(groups))
|
|
109
|
+
continue;
|
|
110
|
+
const keptGroups = [];
|
|
111
|
+
for (const group of groups) {
|
|
112
|
+
const list = Array.isArray(group?.hooks) ? group.hooks : [];
|
|
113
|
+
const kept = list.filter((h) => !isOurs(h));
|
|
114
|
+
if (kept.length > 0)
|
|
115
|
+
keptGroups.push({ ...group, hooks: kept });
|
|
116
|
+
}
|
|
117
|
+
if (keptGroups.length > 0)
|
|
118
|
+
clean[event] = keptGroups;
|
|
119
|
+
}
|
|
120
|
+
return clean;
|
|
121
|
+
}
|
|
122
|
+
/** Remove recursivamente arquivos órfãos sob o namespace, exceto os recém-escritos. */
|
|
123
|
+
function pruneScripts(scriptsDir, keep, dryRun, pruned) {
|
|
124
|
+
if (!existsSync(scriptsDir))
|
|
125
|
+
return;
|
|
126
|
+
const walk = (abs) => {
|
|
127
|
+
for (const entry of readdirSync(abs, { withFileTypes: true })) {
|
|
128
|
+
const childAbs = join(abs, entry.name);
|
|
129
|
+
if (entry.isDirectory()) {
|
|
130
|
+
walk(childAbs);
|
|
131
|
+
try {
|
|
132
|
+
if (readdirSync(childAbs).length === 0 && !dryRun)
|
|
133
|
+
rmSync(childAbs, { recursive: true });
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
/* ignore */
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
else if (entry.isFile()) {
|
|
140
|
+
if (!keep.has(childAbs)) {
|
|
141
|
+
if (!dryRun)
|
|
142
|
+
rmSync(childAbs);
|
|
143
|
+
pruned.push(childAbs);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
walk(scriptsDir);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Provisiona os hooks do noclaf no Claude Code. Idempotente e não-destrutivo.
|
|
152
|
+
* No-op (installed vazio) se o pacote não tem `hooks/` ou nada casa a seleção.
|
|
153
|
+
*/
|
|
154
|
+
export function provisionHooks(options = {}) {
|
|
155
|
+
const dryRun = options.dryRun ?? false;
|
|
156
|
+
const dir = options.skillsDir ?? skillsDir();
|
|
157
|
+
const targetDir = options.targetDir ?? defaultTargetDir();
|
|
158
|
+
const surface = options.surface ?? 'claude-code';
|
|
159
|
+
const settingsPath = join(targetDir, 'settings.json');
|
|
160
|
+
const scriptsDir = join(targetDir, HOOKS_NS);
|
|
161
|
+
const resolved = collectHooks(dir, surface, options.sections);
|
|
162
|
+
const writtenScripts = new Set();
|
|
163
|
+
const installed = [];
|
|
164
|
+
for (const h of resolved) {
|
|
165
|
+
const destAbs = join(scriptsDir, h.role, h.script);
|
|
166
|
+
if (!dryRun) {
|
|
167
|
+
mkdirSync(join(scriptsDir, h.role), { recursive: true });
|
|
168
|
+
copyFileSync(h.srcAbs, destAbs);
|
|
169
|
+
}
|
|
170
|
+
writtenScripts.add(destAbs);
|
|
171
|
+
installed.push({
|
|
172
|
+
event: h.event,
|
|
173
|
+
matcher: h.matcher,
|
|
174
|
+
script: `${h.role}/${h.script}`,
|
|
175
|
+
description: h.description,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
const prunedScripts = [];
|
|
179
|
+
pruneScripts(scriptsDir, writtenScripts, dryRun, prunedScripts);
|
|
180
|
+
// Merge no settings.json: tira as nossas entradas antigas, re-adiciona as atuais.
|
|
181
|
+
const settings = readJson(settingsPath) ?? {};
|
|
182
|
+
const currentHooks = settings.hooks ?? {};
|
|
183
|
+
const merged = stripOurs(currentHooks);
|
|
184
|
+
for (const h of resolved) {
|
|
185
|
+
const commandPath = join(scriptsDir, h.role, h.script).split('\\').join('/');
|
|
186
|
+
const ourHook = { type: 'command', command: `python3 "${commandPath}"` };
|
|
187
|
+
const groups = merged[h.event] ?? (merged[h.event] = []);
|
|
188
|
+
let group = groups.find((g) => g.matcher === h.matcher);
|
|
189
|
+
if (!group) {
|
|
190
|
+
group = h.matcher !== undefined ? { matcher: h.matcher, hooks: [] } : { hooks: [] };
|
|
191
|
+
groups.push(group);
|
|
192
|
+
}
|
|
193
|
+
group.hooks.push(ourHook);
|
|
194
|
+
}
|
|
195
|
+
if (!dryRun) {
|
|
196
|
+
if (Object.keys(merged).length > 0) {
|
|
197
|
+
settings.hooks = merged;
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
delete settings.hooks;
|
|
201
|
+
}
|
|
202
|
+
writeJson(settingsPath, settings);
|
|
203
|
+
}
|
|
204
|
+
return { settingsPath, scriptsDir, dryRun, installed, prunedScripts };
|
|
205
|
+
}
|
|
206
|
+
/** Remove tudo que o noclaf instalou de hooks (scripts + entradas do settings). */
|
|
207
|
+
export function uninstallHooks(options = {}) {
|
|
208
|
+
const dryRun = options.dryRun ?? false;
|
|
209
|
+
const targetDir = options.targetDir ?? defaultTargetDir();
|
|
210
|
+
const settingsPath = join(targetDir, 'settings.json');
|
|
211
|
+
const scriptsDir = join(targetDir, HOOKS_NS);
|
|
212
|
+
const removedScripts = [];
|
|
213
|
+
if (existsSync(scriptsDir)) {
|
|
214
|
+
if (!dryRun)
|
|
215
|
+
rmSync(scriptsDir, { recursive: true, force: true });
|
|
216
|
+
removedScripts.push(scriptsDir);
|
|
217
|
+
}
|
|
218
|
+
const settings = readJson(settingsPath);
|
|
219
|
+
if (settings && settings.hooks) {
|
|
220
|
+
const merged = stripOurs(settings.hooks);
|
|
221
|
+
if (!dryRun) {
|
|
222
|
+
if (Object.keys(merged).length > 0)
|
|
223
|
+
settings.hooks = merged;
|
|
224
|
+
else
|
|
225
|
+
delete settings.hooks;
|
|
226
|
+
writeJson(settingsPath, settings);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return { settingsPath, removedScripts, dryRun };
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=hooks.js.map
|