@promovaweb/specsfy 0.7.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE +21 -0
  3. package/README.md +248 -0
  4. package/bin/specsfy.cjs +3 -0
  5. package/dist/backlog.d.ts +17 -0
  6. package/dist/backlog.d.ts.map +1 -0
  7. package/dist/backlog.js +73 -0
  8. package/dist/backlog.js.map +1 -0
  9. package/dist/catalog.d.ts +33 -0
  10. package/dist/catalog.d.ts.map +1 -0
  11. package/dist/catalog.js +135 -0
  12. package/dist/catalog.js.map +1 -0
  13. package/dist/cli.d.ts +12 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +258 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/config.d.ts +14 -0
  18. package/dist/config.d.ts.map +1 -0
  19. package/dist/config.js +44 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/filesystem.d.ts +19 -0
  22. package/dist/filesystem.d.ts.map +1 -0
  23. package/dist/filesystem.js +67 -0
  24. package/dist/filesystem.js.map +1 -0
  25. package/dist/github.d.ts +8 -0
  26. package/dist/github.d.ts.map +1 -0
  27. package/dist/github.js +36 -0
  28. package/dist/github.js.map +1 -0
  29. package/dist/installer.d.ts +48 -0
  30. package/dist/installer.d.ts.map +1 -0
  31. package/dist/installer.js +613 -0
  32. package/dist/installer.js.map +1 -0
  33. package/dist/main.d.ts +5 -0
  34. package/dist/main.d.ts.map +1 -0
  35. package/dist/main.js +13 -0
  36. package/dist/main.js.map +1 -0
  37. package/dist/progress.d.ts +46 -0
  38. package/dist/progress.d.ts.map +1 -0
  39. package/dist/progress.js +148 -0
  40. package/dist/progress.js.map +1 -0
  41. package/dist/project-testing.d.ts +27 -0
  42. package/dist/project-testing.d.ts.map +1 -0
  43. package/dist/project-testing.js +150 -0
  44. package/dist/project-testing.js.map +1 -0
  45. package/dist/skill-lock.d.ts +23 -0
  46. package/dist/skill-lock.d.ts.map +1 -0
  47. package/dist/skill-lock.js +80 -0
  48. package/dist/skill-lock.js.map +1 -0
  49. package/dist/tui.d.ts +141 -0
  50. package/dist/tui.d.ts.map +1 -0
  51. package/dist/tui.js +1219 -0
  52. package/dist/tui.js.map +1 -0
  53. package/dist/updater.d.ts +41 -0
  54. package/dist/updater.d.ts.map +1 -0
  55. package/dist/updater.js +221 -0
  56. package/dist/updater.js.map +1 -0
  57. package/dist/version.d.ts +7 -0
  58. package/dist/version.d.ts.map +1 -0
  59. package/dist/version.js +7 -0
  60. package/dist/version.js.map +1 -0
  61. package/package.json +58 -0
package/dist/cli.js ADDED
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Entrada pública do Specsfy CLI.
3
+ *
4
+ * Define a gramática de comandos e coordena módulos de domínio sem incorporar
5
+ * regras de instalação, catálogo ou progresso.
6
+ */
7
+ import { Command, CommanderError, Option } from "commander";
8
+ import { createInterface } from "node:readline/promises";
9
+ import { stdin, stdout } from "node:process";
10
+ import { Catalog } from "./catalog.js";
11
+ import { loadConfig, updateConfig } from "./config.js";
12
+ import { SkillInstaller } from "./installer.js";
13
+ import { scanSpecs, serializeSpec, specsFingerprint, summarizeSpecs, } from "./progress.js";
14
+ import { runProjectTests } from "./project-testing.js";
15
+ import { offerStartupUpdate } from "./updater.js";
16
+ import { VERSION } from "./version.js";
17
+ /** Constrói o parser público sem executar efeitos. */
18
+ export function buildProgram() {
19
+ const program = new Command();
20
+ program
21
+ .name("specsfy")
22
+ .description("Instala o Specsfy e acompanha especificações no terminal.")
23
+ .version(VERSION, "--version")
24
+ .showHelpAfterError()
25
+ .action(async () => {
26
+ await openTui(process.cwd());
27
+ });
28
+ program
29
+ .command("install")
30
+ .description("instala as skills do framework")
31
+ .addOption(projectOption())
32
+ .option("--force", "substitui arquivos gerenciados alterados")
33
+ .option("--detected", "instala também os especialistas detectados")
34
+ .option("--specialist <nome>", "especialista explícito; pode ser repetido", collect, [])
35
+ .option("--json", "emite resultado JSON")
36
+ .action(async (options) => {
37
+ const installer = await SkillInstaller.create(options.project, options.force);
38
+ const changed = await installer.installBase();
39
+ let specialistNames = [...options.specialist];
40
+ let catalog;
41
+ if (options.detected) {
42
+ catalog = await Catalog.fetch();
43
+ specialistNames.push(...(await catalog.detect(options.project)).map((entry) => entry.name));
44
+ }
45
+ if (specialistNames.length) {
46
+ catalog ??= await Catalog.fetch();
47
+ specialistNames = catalog.resolve(specialistNames).map((entry) => entry.name);
48
+ changed.push(...(await installer.installSpecialists(specialistNames)));
49
+ }
50
+ printInstallation(changed, Boolean(options.json));
51
+ });
52
+ const skills = program.command("skills").description("gerencia especialistas");
53
+ skills
54
+ .command("list")
55
+ .description("lista o catálogo")
56
+ .option("--json", "emite resultado JSON")
57
+ .action(async (options) => {
58
+ printCatalog((await Catalog.fetch()).entries, Boolean(options.json));
59
+ });
60
+ skills
61
+ .command("detect")
62
+ .description("detecta skills para o projeto")
63
+ .addOption(projectOption())
64
+ .option("--json", "emite resultado JSON")
65
+ .action(async (options) => {
66
+ const catalog = await Catalog.fetch();
67
+ printCatalog(await catalog.detect(options.project), Boolean(options.json));
68
+ });
69
+ skills
70
+ .command("add")
71
+ .description("instala especialistas")
72
+ .argument("<names...>", "nomes das skills")
73
+ .addOption(projectOption())
74
+ .option("--force", "substitui arquivos gerenciados alterados")
75
+ .action(async (names, options) => {
76
+ const catalog = await Catalog.fetch();
77
+ const resolved = catalog.resolve(names).map((entry) => entry.name);
78
+ const installer = await SkillInstaller.create(options.project, options.force);
79
+ printPaths(await installer.installSpecialists(resolved));
80
+ });
81
+ skills
82
+ .command("remove")
83
+ .description("remove skills instaladas")
84
+ .argument("<names...>", "nomes das skills")
85
+ .addOption(projectOption())
86
+ .option("--force", "remove arquivos gerenciados alterados")
87
+ .action(async (names, options) => {
88
+ const installer = await SkillInstaller.create(options.project, options.force);
89
+ printPaths(await installer.remove(names));
90
+ });
91
+ skills
92
+ .command("update")
93
+ .description("atualiza todas as skills Specsfy instaladas")
94
+ .addOption(projectOption())
95
+ .option("--force", "substitui arquivos gerenciados alterados")
96
+ .action(async (options) => {
97
+ const installer = await SkillInstaller.create(options.project, options.force);
98
+ printInstallation(await installer.updateAll(), false);
99
+ });
100
+ program
101
+ .command("progress")
102
+ .description("exibe progresso das specs")
103
+ .addOption(projectOption())
104
+ .option("--json", "emite resultado JSON")
105
+ .option("--watch", "emite novo resumo quando uma spec muda")
106
+ .option("--interval <segundos>", "intervalo do watch em segundos", Number)
107
+ .action(async (options) => runProgress(options));
108
+ program
109
+ .command("test")
110
+ .description("executa os testes do projeto")
111
+ .addOption(projectOption())
112
+ .action(async (options) => {
113
+ const result = await runProjectTests(options.project);
114
+ process.exitCode = result.exit_code;
115
+ });
116
+ program
117
+ .command("tui")
118
+ .description("abre a interface terminal")
119
+ .addOption(projectOption())
120
+ .action(async (options) => {
121
+ await openTui(options.project);
122
+ });
123
+ const config = program.command("config").description("configura o projeto consumidor");
124
+ config
125
+ .command("show")
126
+ .description("exibe a configuração efetiva")
127
+ .addOption(projectOption())
128
+ .option("--json", "emite resultado JSON")
129
+ .action(async (options) => {
130
+ printConfig(await loadConfig(options.project), Boolean(options.json));
131
+ });
132
+ config
133
+ .command("set")
134
+ .description("altera a configuração")
135
+ .addOption(projectOption())
136
+ .requiredOption("--watch-interval <segundos>", "intervalo de atualização", Number)
137
+ .option("--json", "emite resultado JSON")
138
+ .action(async (options) => {
139
+ printConfig(await updateConfig(options.project, options.watchInterval), Boolean(options.json));
140
+ });
141
+ return program;
142
+ }
143
+ /** Executa o CLI e converte falhas conhecidas em exit code previsível. */
144
+ export async function runCli(argv = process.argv) {
145
+ const program = buildProgram();
146
+ program.exitOverride();
147
+ try {
148
+ await program.parseAsync(argv);
149
+ return Number(process.exitCode ?? 0);
150
+ }
151
+ catch (error) {
152
+ if (error instanceof CommanderError) {
153
+ if (error.code === "commander.helpDisplayed" ||
154
+ error.code === "commander.version") {
155
+ return 0;
156
+ }
157
+ return error.exitCode;
158
+ }
159
+ const message = error instanceof Error ? error.message : String(error);
160
+ console.error(`erro: ${message}`);
161
+ return 1;
162
+ }
163
+ }
164
+ function projectOption() {
165
+ return new Option("--project <caminho>", "raiz do projeto consumidor").default(process.cwd());
166
+ }
167
+ function collect(value, previous) {
168
+ return [...previous, value];
169
+ }
170
+ async function openTui(project) {
171
+ const reader = createInterface({ input: stdin, output: stdout });
172
+ const shouldExit = await offerStartupUpdate(VERSION, (prompt) => reader.question(prompt));
173
+ reader.close();
174
+ if (shouldExit)
175
+ return;
176
+ const { SpecsfyTui } = await import("./tui.js");
177
+ await new SpecsfyTui(project).run();
178
+ }
179
+ async function runProgress(options) {
180
+ const configured = await loadConfig(options.project);
181
+ const interval = options.interval ?? configured.watch_interval;
182
+ if (!Number.isFinite(interval) || interval <= 0) {
183
+ throw new Error("interval deve ser maior que zero");
184
+ }
185
+ let previous = "";
186
+ while (true) {
187
+ const fingerprint = await specsFingerprint(options.project);
188
+ if (fingerprint !== previous) {
189
+ const specs = await scanSpecs(options.project);
190
+ printProgress(specs, Boolean(options.json));
191
+ previous = fingerprint;
192
+ if (!options.watch) {
193
+ if (!specs.length)
194
+ process.exitCode = 2;
195
+ return;
196
+ }
197
+ }
198
+ await new Promise((resolve) => setTimeout(resolve, interval * 1000));
199
+ }
200
+ }
201
+ function printProgress(specs, json) {
202
+ const summary = summarizeSpecs(specs);
203
+ if (json) {
204
+ console.log(JSON.stringify({
205
+ summary,
206
+ specs: specs.map(serializeSpec),
207
+ }));
208
+ return;
209
+ }
210
+ console.log(`Resumo\t${summary.total_specs} specs\t` +
211
+ `${summary.completed_tasks}/${summary.total_tasks} tarefas\t` +
212
+ `${summary.completed_items}/${summary.total_items} itens\t` +
213
+ `${summary.percent}%`);
214
+ for (const spec of specs) {
215
+ console.log(`${spec.slug}\t${spec.status}\t` +
216
+ `${spec.completed_tasks}/${spec.total_tasks} tarefas\t` +
217
+ `${spec.completed_items}/${spec.total_items} itens\t${spec.percent}%`);
218
+ }
219
+ }
220
+ function printCatalog(entries, json) {
221
+ if (json) {
222
+ console.log(JSON.stringify(entries.map(({ name, description, category, tags, requires }) => ({
223
+ name,
224
+ description,
225
+ category,
226
+ tags,
227
+ requires,
228
+ }))));
229
+ return;
230
+ }
231
+ for (const entry of entries) {
232
+ console.log(`${entry.name}\t${entry.description}`);
233
+ }
234
+ }
235
+ function printConfig(config, json) {
236
+ if (json)
237
+ console.log(JSON.stringify(config));
238
+ else {
239
+ console.log(`project\t${config.project}`);
240
+ console.log(`watch_interval\t${config.watch_interval}`);
241
+ }
242
+ }
243
+ function printPaths(paths) {
244
+ for (const path of paths)
245
+ console.log(path);
246
+ }
247
+ function printInstallation(paths, json) {
248
+ if (json) {
249
+ console.log(JSON.stringify({ changed: paths.length, paths }));
250
+ }
251
+ else if (paths.length) {
252
+ printPaths(paths);
253
+ }
254
+ else {
255
+ console.log("skills já estão atualizadas");
256
+ }
257
+ }
258
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Configuração persistente por projeto.
3
+ */
4
+ export declare const DEFAULT_WATCH_INTERVAL = 0.75;
5
+ /** Configuração efetiva do projeto. */
6
+ export interface ProjectConfig {
7
+ project: string;
8
+ watch_interval: number;
9
+ }
10
+ /** Carrega a configuração e aplica os valores padrão. */
11
+ export declare function loadConfig(project: string): Promise<ProjectConfig>;
12
+ /** Atualiza o intervalo preservando campos desconhecidos. */
13
+ export declare function updateConfig(project: string, watchInterval: number): Promise<ProjectConfig>;
14
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAOD,yDAAyD;AACzD,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAMxE;AAED,6DAA6D;AAC7D,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,CAAC,CAWxB"}
package/dist/config.js ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Configuração persistente por projeto.
3
+ */
4
+ import { join } from "node:path";
5
+ import { isFile, readJson, resolvePath, writeTextAtomic } from "./filesystem.js";
6
+ export const DEFAULT_WATCH_INTERVAL = 0.75;
7
+ /** Carrega a configuração e aplica os valores padrão. */
8
+ export async function loadConfig(project) {
9
+ const root = resolvePath(project);
10
+ const payload = await readPayload(root);
11
+ const interval = Number(payload.watch_interval ?? DEFAULT_WATCH_INTERVAL);
12
+ validateInterval(interval);
13
+ return { project: root, watch_interval: interval };
14
+ }
15
+ /** Atualiza o intervalo preservando campos desconhecidos. */
16
+ export async function updateConfig(project, watchInterval) {
17
+ const root = resolvePath(project);
18
+ validateInterval(watchInterval);
19
+ const payload = await readPayload(root);
20
+ payload.schema_version = 1;
21
+ payload.watch_interval = watchInterval;
22
+ await writeTextAtomic(join(root, ".specsfy", "config.json"), `${JSON.stringify(payload, null, 2)}\n`);
23
+ return { project: root, watch_interval: watchInterval };
24
+ }
25
+ async function readPayload(project) {
26
+ const path = join(project, ".specsfy", "config.json");
27
+ if (!(await isFile(path)))
28
+ return { schema_version: 1 };
29
+ const payload = await readJson(path);
30
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
31
+ throw new Error(`configuração inválida: ${path}`);
32
+ }
33
+ const value = payload;
34
+ if ((value.schema_version ?? 1) !== 1) {
35
+ throw new Error("versão de configuração não suportada");
36
+ }
37
+ return value;
38
+ }
39
+ function validateInterval(interval) {
40
+ if (!Number.isFinite(interval) || interval <= 0) {
41
+ throw new Error("watch interval deve ser um número finito maior que zero");
42
+ }
43
+ }
44
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEjF,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAa3C,yDAAyD;AACzD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe;IAC9C,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,IAAI,sBAAsB,CAAC,CAAC;IAC1E,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC;AACrD,CAAC;AAED,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,aAAqB;IAErB,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,gBAAgB,CAAC,aAAa,CAAC,CAAC;IAChC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;IACxC,OAAO,CAAC,cAAc,GAAG,CAAC,CAAC;IAC3B,OAAO,CAAC,cAAc,GAAG,aAAa,CAAC;IACvC,MAAM,eAAe,CACnB,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,aAAa,CAAC,EACrC,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CACxC,CAAC;IACF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC;AAC1D,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,OAAe;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;IACtD,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC;IACxD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,KAAK,GAAG,OAAwB,CAAC;IACvC,IAAI,CAAC,KAAK,CAAC,cAAc,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAgB;IACxC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Utilitários compartilhados de filesystem.
3
+ *
4
+ * Centraliza resolução de caminhos, escrita atômica e varreduras pequenas
5
+ * usadas pelo CLI sem esconder operações destrutivas.
6
+ */
7
+ /** Expande `~` e retorna um caminho absoluto normalizado. */
8
+ export declare function resolvePath(value: string): string;
9
+ /** Informa se um caminho existe, sem propagar a ausência como erro. */
10
+ export declare function pathExists(path: string): Promise<boolean>;
11
+ /** Informa se um caminho existente é um arquivo regular. */
12
+ export declare function isFile(path: string): Promise<boolean>;
13
+ /** Escreve texto UTF-8 por arquivo temporário no mesmo diretório. */
14
+ export declare function writeTextAtomic(target: string, content: string, mode?: number): Promise<void>;
15
+ /** Lê JSON e mantém o tipo como desconhecido até a validação do chamador. */
16
+ export declare function readJson(path: string): Promise<unknown>;
17
+ /** Lista arquivos recursivamente em ordem estável. */
18
+ export declare function listFiles(root: string): Promise<string[]>;
19
+ //# sourceMappingURL=filesystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../src/filesystem.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAeH,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED,uEAAuE;AACvE,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED,4DAA4D;AAC5D,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAM3D;AAED,qEAAqE;AACrE,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,6EAA6E;AAC7E,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE7D;AAED,sDAAsD;AACtD,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAc/D"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Utilitários compartilhados de filesystem.
3
+ *
4
+ * Centraliza resolução de caminhos, escrita atômica e varreduras pequenas
5
+ * usadas pelo CLI sem esconder operações destrutivas.
6
+ */
7
+ import { constants } from "node:fs";
8
+ import { access, mkdir, readFile, readdir, rename, stat, writeFile, } from "node:fs/promises";
9
+ import { homedir } from "node:os";
10
+ import { basename, dirname, isAbsolute, join, resolve } from "node:path";
11
+ /** Expande `~` e retorna um caminho absoluto normalizado. */
12
+ export function resolvePath(value) {
13
+ const expanded = value === "~"
14
+ ? homedir()
15
+ : value.startsWith("~/")
16
+ ? join(homedir(), value.slice(2))
17
+ : value;
18
+ return isAbsolute(expanded) ? resolve(expanded) : resolve(process.cwd(), expanded);
19
+ }
20
+ /** Informa se um caminho existe, sem propagar a ausência como erro. */
21
+ export async function pathExists(path) {
22
+ try {
23
+ await access(path, constants.F_OK);
24
+ return true;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ /** Informa se um caminho existente é um arquivo regular. */
31
+ export async function isFile(path) {
32
+ try {
33
+ return (await stat(path)).isFile();
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ /** Escreve texto UTF-8 por arquivo temporário no mesmo diretório. */
40
+ export async function writeTextAtomic(target, content, mode) {
41
+ await mkdir(dirname(target), { recursive: true });
42
+ const temporary = join(dirname(target), `.${basename(target) || "arquivo"}.${process.pid}.tmp`);
43
+ await writeFile(temporary, content, { encoding: "utf8", mode });
44
+ await rename(temporary, target);
45
+ }
46
+ /** Lê JSON e mantém o tipo como desconhecido até a validação do chamador. */
47
+ export async function readJson(path) {
48
+ return JSON.parse(await readFile(path, "utf8"));
49
+ }
50
+ /** Lista arquivos recursivamente em ordem estável. */
51
+ export async function listFiles(root) {
52
+ if (!(await pathExists(root))) {
53
+ return [];
54
+ }
55
+ const output = [];
56
+ for (const entry of await readdir(root, { withFileTypes: true })) {
57
+ const path = join(root, entry.name);
58
+ if (entry.isDirectory()) {
59
+ output.push(...(await listFiles(path)));
60
+ }
61
+ else if (entry.isFile() || entry.isSymbolicLink()) {
62
+ output.push(path);
63
+ }
64
+ }
65
+ return output.sort();
66
+ }
67
+ //# sourceMappingURL=filesystem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.js","sourceRoot":"","sources":["../src/filesystem.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Autenticação compartilhada para chamadas à API do GitHub.
3
+ */
4
+ /** Monta os headers oficiais e inclui token quando disponível. */
5
+ export declare function apiHeaders(userAgent: string, accept?: string): Promise<Record<string, string>>;
6
+ /** Resolve a credencial sem persistir nem imprimir seu valor. */
7
+ export declare function githubToken(): Promise<string | undefined>;
8
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,kEAAkE;AAClE,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,MAAM,SAAgC,GACrC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CASjC;AAED,iEAAiE;AACjE,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAa/D"}
package/dist/github.js ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Autenticação compartilhada para chamadas à API do GitHub.
3
+ */
4
+ import { execFile } from "node:child_process";
5
+ import { promisify } from "node:util";
6
+ const execFileAsync = promisify(execFile);
7
+ /** Monta os headers oficiais e inclui token quando disponível. */
8
+ export async function apiHeaders(userAgent, accept = "application/vnd.github+json") {
9
+ const headers = {
10
+ Accept: accept,
11
+ "User-Agent": userAgent,
12
+ "X-GitHub-Api-Version": "2022-11-28",
13
+ };
14
+ const token = await githubToken();
15
+ if (token)
16
+ headers.Authorization = `Bearer ${token}`;
17
+ return headers;
18
+ }
19
+ /** Resolve a credencial sem persistir nem imprimir seu valor. */
20
+ export async function githubToken() {
21
+ for (const variable of ["GH_TOKEN", "GITHUB_TOKEN"]) {
22
+ const value = process.env[variable]?.trim();
23
+ if (value)
24
+ return value;
25
+ }
26
+ try {
27
+ const result = await execFileAsync("gh", ["auth", "token"], {
28
+ encoding: "utf8",
29
+ });
30
+ return result.stdout.trim() || undefined;
31
+ }
32
+ catch {
33
+ return undefined;
34
+ }
35
+ }
36
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,SAAiB,EACjB,MAAM,GAAG,6BAA6B;IAEtC,MAAM,OAAO,GAA2B;QACtC,MAAM,EAAE,MAAM;QACd,YAAY,EAAE,SAAS;QACvB,sBAAsB,EAAE,YAAY;KACrC,CAAC;IACF,MAAM,KAAK,GAAG,MAAM,WAAW,EAAE,CAAC;IAClC,IAAI,KAAK;QAAE,OAAO,CAAC,aAAa,GAAG,UAAU,KAAK,EAAE,CAAC;IACrD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,KAAK,MAAM,QAAQ,IAAI,CAAC,UAAU,EAAE,cAAc,CAAU,EAAE,CAAC;QAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC;QAC5C,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE;YAC1D,QAAQ,EAAE,MAAM;SACjB,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Instalação idempotente do framework e das skills do Specsfy.
3
+ *
4
+ * A materialização é delegada ao CLI oficial `skills`; este módulo mantém os
5
+ * fingerprints, protege customizações e gerencia somente blocos identificados.
6
+ */
7
+ export declare const MONOREPO_REPOSITORY = "https://github.com/promovaweb/specsfy.git";
8
+ export declare const BASE_DIRECTORY = "skills";
9
+ export declare const SPECIALISTS_DIRECTORY = "specialists";
10
+ export declare const BASE_SKILLS: readonly ["specsfy-01-inbox", "specsfy-02-backlog", "specsfy-03-specify", "specsfy-04-validate", "specsfy-05-tasks", "specsfy-06-tdd-bdd", "specsfy-07-implement", "specsfy-update-spec", "specsfy-progress"];
11
+ export declare const AUXILIARY_SKILLS: readonly ["specsfy-aux-stack", "specsfy-aux-rules", "specsfy-aux-database"];
12
+ export declare const DOCUMENTATION_SKILLS: readonly ["specsfy-documentator"];
13
+ export declare const FRAMEWORK_SKILLS: readonly ["specsfy-setup", "specsfy-aux-stack", "specsfy-aux-rules", "specsfy-aux-database", "specsfy-documentator", "specsfy-01-inbox", "specsfy-02-backlog", "specsfy-03-specify", "specsfy-04-validate", "specsfy-05-tasks", "specsfy-06-tdd-bdd", "specsfy-07-implement", "specsfy-update-spec", "specsfy-progress"];
14
+ export declare const RENAMED_BASE_SKILLS: Readonly<Record<string, string>>;
15
+ /** Instalador de um projeto consumidor explicitamente selecionado. */
16
+ export declare class SkillInstaller {
17
+ readonly project: string;
18
+ readonly force: boolean;
19
+ private constructor();
20
+ /** Valida o destino e cria o lock oficial antes de liberar o instalador. */
21
+ static create(project: string, force?: boolean): Promise<SkillInstaller>;
22
+ /** Instala todo o framework base. */
23
+ installBase(): Promise<string[]>;
24
+ /** Instala um subconjunto válido do framework base. */
25
+ installBaseSelection(names: string[]): Promise<string[]>;
26
+ /** Instala framework estrutural e skills a partir de uma origem local. */
27
+ installBaseFromCheckout(checkout: string, names?: readonly string[]): Promise<string[]>;
28
+ /** Publica regras, templates, exemplo e blocos gerenciados. */
29
+ installFrameworkFromCheckout(checkout: string): Promise<string[]>;
30
+ /** Instala especialistas do catálogo oficial. */
31
+ installSpecialists(names: string[]): Promise<string[]>;
32
+ /** Atualiza todas as skills Specsfy já registradas. */
33
+ updateAll(): Promise<string[]>;
34
+ /** Clona uma origem temporária e instala as skills solicitadas. */
35
+ installFromRepository(repository: string, names: readonly string[], sourceName: string, directory?: string): Promise<string[]>;
36
+ /** Instala skills locais preservando alterações não autorizadas. */
37
+ installFromCheckout(checkout: string, names: readonly string[], sourceName: string): Promise<string[]>;
38
+ /** Remove somente nomes pertencentes ao Specsfy e protege customizações. */
39
+ remove(names: string[]): Promise<string[]>;
40
+ private validateSkillInstallation;
41
+ private legacyBaseTargets;
42
+ private removeLegacyBase;
43
+ private readLock;
44
+ private writeLock;
45
+ }
46
+ /** Calcula o fingerprint estável de uma skill, ignorando caches gerados. */
47
+ export declare function skillDigest(path: string): Promise<string>;
48
+ //# sourceMappingURL=installer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgCH,eAAO,MAAM,mBAAmB,8CACa,CAAC;AAC9C,eAAO,MAAM,cAAc,WAAW,CAAC;AACvC,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AACnD,eAAO,MAAM,WAAW,YACtB,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,CACV,CAAC;AACX,eAAO,MAAM,gBAAgB,YAC3B,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,CACd,CAAC;AACX,eAAO,MAAM,oBAAoB,YAAI,sBAAsB,CAAU,CAAC;AACtE,eAAO,MAAM,gBAAgB,0TAKnB,CAAC;AACX,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYhE,CAAC;AAmCF,sEAAsE;AACtE,qBAAa,cAAc;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAExB,OAAO,eAGN;IAED,4EAA4E;IAC5E,OAAa,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,CAK3E;IAED,qCAAqC;IAC/B,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAErC;IAED,uDAAuD;IACjD,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAW7D;IAED,0EAA0E;IACpE,uBAAuB,CAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,SAAS,MAAM,EAAgB,GACrC,OAAO,CAAC,MAAM,EAAE,CAAC,CASnB;IAED,+DAA+D;IACzD,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAwHtE;IAED,iDAAiD;IAC3C,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAO3D;IAED,uDAAuD;IACjD,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAYnC;IAED,mEAAmE;IAC7D,qBAAqB,CACzB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAInB;IAED,oEAAoE;IAC9D,mBAAmB,CACvB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CA6EnB;IAED,4EAA4E;IACtE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAsD/C;YAEa,yBAAyB;YAuBzB,iBAAiB;YAoBjB,gBAAgB;YAsBhB,QAAQ;YAeR,SAAS;CAMxB;AAmMD,4EAA4E;AAC5E,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAqB/D"}