pipcode 0.0.0-stage → 2.1.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/LICENSE +47 -0
- package/README.md +44 -2
- package/auto.js +40 -0
- package/bin/setup.js +31 -0
- package/bin/teardown.js +18 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/index.js +39 -0
- package/package.json +45 -4
- package/src/auth/auth-email.js +32 -0
- package/src/auth/trusted-key.js +13 -0
- package/src/auto/adjust.js +41 -0
- package/src/auto/amnesia.js +37 -0
- package/src/auto/anonymize-tasks.js +13 -0
- package/src/auto/apply-diff-backed.js +10 -0
- package/src/auto/apply-diff.js +60 -0
- package/src/auto/ask-guard.js +16 -0
- package/src/auto/ask-pause.js +36 -0
- package/src/auto/ask-protocol.js +6 -0
- package/src/auto/ask-user.js +29 -0
- package/src/auto/backup-touched.js +30 -0
- package/src/auto/budget.js +52 -0
- package/src/auto/cancel.js +20 -0
- package/src/auto/constants.js +30 -0
- package/src/auto/crypto-util.js +17 -0
- package/src/auto/decompose.js +47 -0
- package/src/auto/diff.js +19 -0
- package/src/auto/fs-list.js +34 -0
- package/src/auto/git-base.js +16 -0
- package/src/auto/git-init.js +39 -0
- package/src/auto/git-restore.js +34 -0
- package/src/auto/git-scope.js +10 -0
- package/src/auto/git.js +38 -0
- package/src/auto/mission-docs.js +29 -0
- package/src/auto/mission-line.js +22 -0
- package/src/auto/node-check.js +30 -0
- package/src/auto/observe.js +23 -0
- package/src/auto/orchestrator.js +30 -0
- package/src/auto/patch-ask.js +16 -0
- package/src/auto/paths.js +18 -0
- package/src/auto/plan.js +42 -0
- package/src/auto/pricing.js +49 -0
- package/src/auto/project-files.js +12 -0
- package/src/auto/prompts.js +24 -0
- package/src/auto/quote-format.js +33 -0
- package/src/auto/retry.js +40 -0
- package/src/auto/run-lock.js +40 -0
- package/src/auto/run-loop.js +38 -0
- package/src/auto/run.js +49 -0
- package/src/auto/snapshot-gate.js +51 -0
- package/src/auto/snapshot-paths.js +33 -0
- package/src/auto/snapshot-restore.js +59 -0
- package/src/auto/snapshot-take.js +30 -0
- package/src/auto/state.js +52 -0
- package/src/auto/step-commit.js +46 -0
- package/src/auto/step-green.js +38 -0
- package/src/auto/step-loop.js +56 -0
- package/src/auto/step-prep.js +60 -0
- package/src/auto/step-qa.js +53 -0
- package/src/auto/step.js +30 -0
- package/src/auto/stop-options.js +34 -0
- package/src/auto/stop-report.js +17 -0
- package/src/auto/tasks.js +44 -0
- package/src/cli/apply-loop.js +29 -0
- package/src/cli/apply-toml.js +26 -0
- package/src/cli/apply.js +45 -0
- package/src/cli/ask-prepare.js +37 -0
- package/src/cli/auth-branches.js +53 -0
- package/src/cli/auth-interactive.js +46 -0
- package/src/cli/auto-ask.js +33 -0
- package/src/cli/auto-audit.js +16 -0
- package/src/cli/auto-cli.js +46 -0
- package/src/cli/auto-consent.js +22 -0
- package/src/cli/auto-flags.js +39 -0
- package/src/cli/auto-gate-session.js +27 -0
- package/src/cli/auto-gate.js +34 -0
- package/src/cli/auto-loader.js +17 -0
- package/src/cli/auto-plan.js +36 -0
- package/src/cli/auto-preflight.js +35 -0
- package/src/cli/auto-quote.js +28 -0
- package/src/cli/auto-report.js +36 -0
- package/src/cli/auto-resume.js +28 -0
- package/src/cli/auto-rules.js +20 -0
- package/src/cli/auto-run.js +26 -0
- package/src/cli/auto-task.js +26 -0
- package/src/cli/banner.js +20 -0
- package/src/cli/centralize.js +23 -0
- package/src/cli/collect-step.js +25 -0
- package/src/cli/config-pairs.js +46 -0
- package/src/cli/config.js +51 -0
- package/src/cli/detect-os.js +11 -0
- package/src/cli/dispatch.js +36 -0
- package/src/cli/env-file.js +44 -0
- package/src/cli/env-load.js +28 -0
- package/src/cli/env-parse.js +39 -0
- package/src/cli/env-rebuild.js +33 -0
- package/src/cli/gitignore-env.js +46 -0
- package/src/cli/help.js +28 -0
- package/src/cli/ide-entries.js +42 -0
- package/src/cli/ide-paths.js +37 -0
- package/src/cli/ide-roots.js +25 -0
- package/src/cli/interactive.js +26 -0
- package/src/cli/jetbrains.js +40 -0
- package/src/cli/legacy-entry.js +17 -0
- package/src/cli/main.js +42 -0
- package/src/cli/memory-cli.js +15 -0
- package/src/cli/prompts.js +24 -0
- package/src/cli/server-entry.js +42 -0
- package/src/cli/setup-args.js +39 -0
- package/src/cli/target-manual.js +24 -0
- package/src/cli/target-select.js +51 -0
- package/src/cli/toml-merge.js +56 -0
- package/src/cli/trial-check.js +15 -0
- package/src/cli/welcome.js +7 -0
- package/src/core/abort-signal.js +38 -0
- package/src/core/auto-deps.js +29 -0
- package/src/core/cache-ttl.js +21 -0
- package/src/core/config-base.js +38 -0
- package/src/core/config-cache.js +50 -0
- package/src/core/config.js +58 -0
- package/src/core/constants.js +31 -0
- package/src/core/crypto.js +45 -0
- package/src/core/dlp.js +51 -0
- package/src/core/doctrine-all.js +31 -0
- package/src/core/doctrine-caches.js +10 -0
- package/src/core/doctrine-fetch.js +49 -0
- package/src/core/doctrine-one.js +18 -0
- package/src/core/doctrine.js +39 -0
- package/src/core/dual-stream-parser.js +55 -0
- package/src/core/exports.js +28 -0
- package/src/core/google-route.js +17 -0
- package/src/core/identity.js +23 -0
- package/src/core/lang.js +19 -0
- package/src/core/leak-guard.js +40 -0
- package/src/core/llm-builder-anthropic.js +26 -0
- package/src/core/llm-builder-gemini.js +22 -0
- package/src/core/llm-builder-openai.js +26 -0
- package/src/core/llm-call.js +53 -0
- package/src/core/llm-config-secondary.js +28 -0
- package/src/core/llm-config.js +32 -0
- package/src/core/llm-errors.js +25 -0
- package/src/core/llm-exchange.js +27 -0
- package/src/core/llm-extract.js +52 -0
- package/src/core/llm-label.js +33 -0
- package/src/core/llm-ollama-request.js +32 -0
- package/src/core/llm-ollama.js +42 -0
- package/src/core/llm-prompts.js +14 -0
- package/src/core/llm-providers.js +26 -0
- package/src/core/llm-request.js +17 -0
- package/src/core/llm-retry-429.js +23 -0
- package/src/core/llm-stream-events.js +29 -0
- package/src/core/llm-stream.js +42 -0
- package/src/core/llm-unbounded.js +15 -0
- package/src/core/llm-warmup.js +44 -0
- package/src/core/manifest.js +42 -0
- package/src/core/master-prompt.js +11 -0
- package/src/core/model-id.js +19 -0
- package/src/core/ollama-lines.js +48 -0
- package/src/core/ollama.js +43 -0
- package/src/core/pkg-manager.js +35 -0
- package/src/core/process-guards.js +47 -0
- package/src/core/progress-emit.js +25 -0
- package/src/core/purge-key.js +20 -0
- package/src/core/reasoning-cut.js +19 -0
- package/src/core/reasoning-models.js +8 -0
- package/src/core/session-net.js +60 -0
- package/src/core/session-resilient.js +44 -0
- package/src/core/session.js +49 -0
- package/src/core/sse-parser.js +25 -0
- package/src/core/stdout-guard.js +11 -0
- package/src/core/tier.js +48 -0
- package/src/core/trial-resolve.js +22 -0
- package/src/core/trial-session.js +60 -0
- package/src/core/trial-status.js +35 -0
- package/src/core/usage-cost.js +25 -0
- package/src/core/usage-extract.js +28 -0
- package/src/core/usage-meter.js +41 -0
- package/src/engine/analyze-need.js +24 -0
- package/src/engine/analyze-ux.js +42 -0
- package/src/engine/ask-history.js +43 -0
- package/src/engine/ask-llm.js +40 -0
- package/src/engine/ask-prompt.js +48 -0
- package/src/engine/ask-rules.js +13 -0
- package/src/engine/ast-call-rules-danger.js +24 -0
- package/src/engine/ast-call-rules-inject.js +29 -0
- package/src/engine/ast-context.js +28 -0
- package/src/engine/ast-import-helpers.js +46 -0
- package/src/engine/ast-lang.js +24 -0
- package/src/engine/ast-node-helpers.js +51 -0
- package/src/engine/ast-rules-calls.js +16 -0
- package/src/engine/ast-rules-graph.js +58 -0
- package/src/engine/ast-rules-js.js +30 -0
- package/src/engine/ast-rules-jsx-py.js +26 -0
- package/src/engine/ast-runtime.js +60 -0
- package/src/engine/ast-scan.js +48 -0
- package/src/engine/audit-citations.js +28 -0
- package/src/engine/audit-facts.js +47 -0
- package/src/engine/audit-report.js +40 -0
- package/src/engine/audit-task.js +37 -0
- package/src/engine/cap-cache.js +50 -0
- package/src/engine/cap-eval.js +49 -0
- package/src/engine/cap-http.js +32 -0
- package/src/engine/cap-meta-anthropic.js +15 -0
- package/src/engine/cap-meta-ollama.js +15 -0
- package/src/engine/cap-meta-openrouter.js +19 -0
- package/src/engine/cap-meta.js +19 -0
- package/src/engine/cap-probe.js +34 -0
- package/src/engine/cap-types.js +29 -0
- package/src/engine/chat-doctrine.js +31 -0
- package/src/engine/checkpoint-control.js +23 -0
- package/src/engine/checkpoint-detect.js +19 -0
- package/src/engine/checkpoint-rules.js +19 -0
- package/src/engine/code-decls.js +12 -0
- package/src/engine/code-graph.js +52 -0
- package/src/engine/coerce.js +8 -0
- package/src/engine/collect.js +21 -0
- package/src/engine/compute-impact.js +17 -0
- package/src/engine/context-files.js +42 -0
- package/src/engine/context-fit.js +28 -0
- package/src/engine/context-margin.js +37 -0
- package/src/engine/context-relay.js +60 -0
- package/src/engine/control-duo.js +38 -0
- package/src/engine/control-phase.js +32 -0
- package/src/engine/control-prompt.js +25 -0
- package/src/engine/control-verdict.js +36 -0
- package/src/engine/default-steps.js +7 -0
- package/src/engine/deps-parse-js.js +49 -0
- package/src/engine/deps-parse-more.js +30 -0
- package/src/engine/deps-parse-other.js +41 -0
- package/src/engine/deps-parse.js +31 -0
- package/src/engine/det-builders-1.js +38 -0
- package/src/engine/det-builders-2.js +50 -0
- package/src/engine/detect-domains.js +27 -0
- package/src/engine/diff-files.js +35 -0
- package/src/engine/doc-suite.js +39 -0
- package/src/engine/doctrine-prefix.js +13 -0
- package/src/engine/domain-regex.js +11 -0
- package/src/engine/exports.js +35 -0
- package/src/engine/extract-deterministic.js +33 -0
- package/src/engine/extract-llm.js +48 -0
- package/src/engine/facts-binary.js +12 -0
- package/src/engine/facts-embedded.js +18 -0
- package/src/engine/facts-functions.js +21 -0
- package/src/engine/facts-html.js +22 -0
- package/src/engine/facts-inventory.js +33 -0
- package/src/engine/facts-lang.js +34 -0
- package/src/engine/facts-measure.js +43 -0
- package/src/engine/facts-patterns.js +28 -0
- package/src/engine/facts-project.js +26 -0
- package/src/engine/facts-size.js +26 -0
- package/src/engine/facts-wordpress.js +36 -0
- package/src/engine/generate-architecture-guide.js +46 -0
- package/src/engine/generate-readme.js +38 -0
- package/src/engine/generate-runbook.js +47 -0
- package/src/engine/git-facts.js +16 -0
- package/src/engine/history-purge.js +16 -0
- package/src/engine/json-balance.js +37 -0
- package/src/engine/json-safe.js +13 -0
- package/src/engine/line-numbers.js +16 -0
- package/src/engine/md-build-mid.js +35 -0
- package/src/engine/md-build-top.js +58 -0
- package/src/engine/md-build.js +36 -0
- package/src/engine/md-parse-helpers.js +7 -0
- package/src/engine/md-parse-pillars.js +38 -0
- package/src/engine/md-parse-ux-design.js +31 -0
- package/src/engine/md-parse.js +33 -0
- package/src/engine/mission-context-prefix.js +30 -0
- package/src/engine/mission-guard.js +36 -0
- package/src/engine/mission-helpers.js +23 -0
- package/src/engine/mission-log.js +56 -0
- package/src/engine/mission-paths.js +7 -0
- package/src/engine/mission-store.js +51 -0
- package/src/engine/normalize-extraction.js +35 -0
- package/src/engine/normalize-impact-design.js +39 -0
- package/src/engine/normalize-pillars.js +36 -0
- package/src/engine/orchestrate-route.js +39 -0
- package/src/engine/orchestrator-build.js +37 -0
- package/src/engine/orchestrator-phases.js +55 -0
- package/src/engine/orchestrator.js +40 -0
- package/src/engine/osv.js +43 -0
- package/src/engine/parse-json-loose.js +36 -0
- package/src/engine/patch-file-sections.js +28 -0
- package/src/engine/patch-scan-cheat.js +52 -0
- package/src/engine/patch-scan-prep.js +37 -0
- package/src/engine/patch-scan-rules.js +51 -0
- package/src/engine/patch-scan.js +38 -0
- package/src/engine/patch-secret-guard.js +50 -0
- package/src/engine/patch-ui-guard.js +49 -0
- package/src/engine/pattern-sanitize.js +23 -0
- package/src/engine/plan-draft.js +37 -0
- package/src/engine/plan-mission.js +60 -0
- package/src/engine/plan-reply-legacy.js +34 -0
- package/src/engine/plan-reply-pillars.js +34 -0
- package/src/engine/plan-reply-ux.js +17 -0
- package/src/engine/plan-reply.js +28 -0
- package/src/engine/plan-summary.js +16 -0
- package/src/engine/preflight-text.js +27 -0
- package/src/engine/preflight.js +27 -0
- package/src/engine/project-brief.js +41 -0
- package/src/engine/project-context.js +49 -0
- package/src/engine/project-instructions.js +19 -0
- package/src/engine/py-imports.js +31 -0
- package/src/engine/relay-analysis.js +44 -0
- package/src/engine/relay-digest.js +42 -0
- package/src/engine/route-llm.js +25 -0
- package/src/engine/rule-text.js +12 -0
- package/src/engine/self-assessment.js +33 -0
- package/src/engine/steps-block.js +19 -0
- package/src/engine/ux-violations-leak.js +43 -0
- package/src/engine/vuln-facts.js +37 -0
- package/src/fs/atomic-text.js +45 -0
- package/src/fs/atomic-write.js +21 -0
- package/src/fs/backup-once.js +27 -0
- package/src/fs/config-path.js +39 -0
- package/src/fs/config-read.js +23 -0
- package/src/fs/config-write.js +60 -0
- package/src/fs/home-root.js +55 -0
- package/src/fs/load-config.js +36 -0
- package/src/fs/os-errors.js +26 -0
- package/src/fs/permissions.js +48 -0
- package/src/mcp/boot-warmup.js +9 -0
- package/src/mcp/call-tool.js +20 -0
- package/src/mcp/client-info.js +35 -0
- package/src/mcp/engine-rules.js +16 -0
- package/src/mcp/handle-dlp.js +37 -0
- package/src/mcp/handle-licence.js +46 -0
- package/src/mcp/handle-list.js +15 -0
- package/src/mcp/handle-memory.js +37 -0
- package/src/mcp/handle-skill-fetch.js +50 -0
- package/src/mcp/handle-skill.js +38 -0
- package/src/mcp/licence-text.js +29 -0
- package/src/mcp/prompt-licence.js +11 -0
- package/src/mcp/server-create.js +33 -0
- package/src/mcp/server-main.js +39 -0
- package/src/mcp/tool-dispatch.js +28 -0
- package/src/mcp/tool-dlp.js +20 -0
- package/src/mcp/tool-licence.js +17 -0
- package/src/mcp/tool-memory.js +32 -0
- package/src/mcp/tool-skill.js +24 -0
- package/src/mcp/tools-list.js +13 -0
- package/src/memory/exports.js +5 -0
- package/src/memory/memory-filter.js +32 -0
- package/src/memory/memory-gate.js +21 -0
- package/src/memory/memory-store.js +44 -0
- package/src/net/auth-browser.js +49 -0
- package/src/net/auth-endpoints.js +33 -0
- package/src/net/local-fetch.js +31 -0
- package/src/net/loopback.js +8 -0
- package/src/net/proxy-env.js +59 -0
- package/src/project-memory/batches.js +17 -0
- package/src/project-memory/cahiers.js +42 -0
- package/src/project-memory/decision-settle.js +15 -0
- package/src/project-memory/decisions.js +36 -0
- package/src/project-memory/index-build.js +35 -0
- package/src/project-memory/memory-build.js +16 -0
- package/src/project-memory/memory-tags.js +40 -0
- package/src/project-memory/pending.js +24 -0
- package/src/project-memory/qa-apply.js +39 -0
- package/src/project-memory/qa-tags.js +28 -0
- package/src/project-memory/recall.js +38 -0
- package/src/project-memory/store.js +37 -0
- package/src/project-memory/window.js +13 -0
- package/src/rules/rules-advisor.js +17 -0
- package/src/rules/rules-frugality.js +17 -0
- package/src/rules/rules-load.js +56 -0
- package/src/rules/rules-project.js +16 -0
- package/src/rules/rules-registry.js +32 -0
- package/src/rules/rules-validate-pricing.js +22 -0
- package/src/rules/rules-validate-scan.js +45 -0
- package/src/rules/rules-validate.js +53 -0
- package/src/teardown/consent.js +31 -0
- package/src/teardown/flags.js +39 -0
- package/src/teardown/main.js +41 -0
- package/src/teardown/plan.js +31 -0
- package/src/teardown/remove-entry.js +39 -0
- package/src/teardown/remove-file.js +37 -0
- package/src/teardown/remove-toml.js +33 -0
- package/src/teardown/report.js +34 -0
- package/src/types/ast.d.ts +40 -0
- package/src/types/auto-ask.d.ts +6 -0
- package/src/types/auto-orch.d.ts +59 -0
- package/src/types/auto.d.ts +45 -0
- package/src/types/capability.d.ts +12 -0
- package/src/types/cli-auto-gate.d.ts +45 -0
- package/src/types/cli-auto.d.ts +42 -0
- package/src/types/config.d.ts +53 -0
- package/src/types/control.d.ts +56 -0
- package/src/types/docs.d.ts +6 -0
- package/src/types/doctrine.d.ts +22 -0
- package/src/types/llm.d.ts +28 -0
- package/src/types/mcp.d.ts +32 -0
- package/src/types/memory.d.ts +17 -0
- package/src/types/mission.d.ts +46 -0
- package/src/types/orchestrator.d.ts +33 -0
- package/src/types/routing.d.ts +25 -0
- package/src/types/rules.d.ts +30 -0
- package/src/types/snapshots.d.ts +25 -0
- package/src/types/teardown.d.ts +13 -0
- package/src/types/usage.d.ts +30 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/cli/dispatch.js — Aiguillage des sous-commandes de `pipcode` (setup, teardown, config,
|
|
2
|
+
// auto, aide). Extrait de bin/setup.js : le point d'entrée reste un simple
|
|
3
|
+
// réorienteur (< 50 lignes). Le CLI ne fait QUE configurer le serveur MCP (setup,
|
|
4
|
+
// config) et exposer le Mode Auto : aucune surface interactive propre.
|
|
5
|
+
|
|
6
|
+
import { main } from "./main.js";
|
|
7
|
+
import { runAutoCli } from "./auto-cli.js";
|
|
8
|
+
import { runMemoryCli } from "./memory-cli.js";
|
|
9
|
+
import { runConfigCli } from "./config.js";
|
|
10
|
+
import { runTrialCheck } from "./trial-check.js";
|
|
11
|
+
import { runTeardown } from "../teardown/main.js";
|
|
12
|
+
import { printAutoHelp, printTopHelp } from "./help.js";
|
|
13
|
+
|
|
14
|
+
/** @param {string} name @returns {(err: Error) => void} */
|
|
15
|
+
const failWith = (name) => (err) => { console.error(`\n ✗ ${name} a échoué :`, err.message); process.exit(1); };
|
|
16
|
+
|
|
17
|
+
/** @param {string[]} argv process.argv complet (argv[2] = sous-commande) @returns {void} */
|
|
18
|
+
export function dispatch(argv) {
|
|
19
|
+
const sub = (argv[2] || "setup").toLowerCase().replace(/^--?/, "");
|
|
20
|
+
if (sub === "setup") main().catch(failWith("setup"));
|
|
21
|
+
else if (sub === "config") runConfigCli().catch(failWith("config"));
|
|
22
|
+
else if (sub === "trial") runTrialCheck().catch(failWith("trial"));
|
|
23
|
+
else if (sub === "teardown") runTeardown({ argv: argv.slice(3) }).catch(failWith("teardown"));
|
|
24
|
+
else if (argv[2] === "memory") runMemoryCli((argv[3] || "").toLowerCase(), argv.slice(4)).catch(failWith("memory"));
|
|
25
|
+
else if (argv[2] === "auto") {
|
|
26
|
+
const action = (argv[3] || "").toLowerCase();
|
|
27
|
+
if (!action || ["--help", "-h", "help"].includes(action)) { printAutoHelp(); process.exit(0); }
|
|
28
|
+
runAutoCli(action, argv.slice(4)).catch(failWith("auto"));
|
|
29
|
+
} else if (["--help", "-h", "help"].includes(argv[2] || "")) {
|
|
30
|
+
printTopHelp(); process.exit(0);
|
|
31
|
+
} else {
|
|
32
|
+
console.error(`Sous-commande inconnue : "${argv[2] || ""}". Essayez : npx pipcode setup`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// src/cli/env-file.js — Fichier .env local du projet (racine = cwd) : lecture
|
|
2
|
+
// fail-safe et écriture atomique 0600 (le fichier porte des secrets).
|
|
3
|
+
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { tmpNameFor } from "../fs/atomic-write.js";
|
|
7
|
+
import { restrictSecretPerms } from "../fs/permissions.js";
|
|
8
|
+
|
|
9
|
+
/** @typedef {Error & { code?: string }} NodeErr */
|
|
10
|
+
|
|
11
|
+
export const ENV_FILE_NAME = ".env";
|
|
12
|
+
|
|
13
|
+
/** @param {string} [cwd] @returns {string} */
|
|
14
|
+
export function localEnvPath(cwd = process.cwd()) {
|
|
15
|
+
return path.join(cwd, ENV_FILE_NAME);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Lit le .env du projet (null si absent). Illisible → erreur explicite.
|
|
19
|
+
/** @param {string} [cwd] @returns {Promise<string | null>} */
|
|
20
|
+
export async function readLocalEnvText(cwd = process.cwd()) {
|
|
21
|
+
try {
|
|
22
|
+
return await fs.readFile(localEnvPath(cwd), "utf8");
|
|
23
|
+
} catch (e) {
|
|
24
|
+
const err = /** @type {NodeErr} */ (e);
|
|
25
|
+
if (err.code === "ENOENT") return null;
|
|
26
|
+
throw new Error(`Lecture de ${localEnvPath(cwd)} impossible : ${err.message}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Écriture atomique (tmp à entropie + rename, fallback Windows) durcie 0600.
|
|
31
|
+
/** @param {string} cwd @param {string} text @returns {Promise<void>} */
|
|
32
|
+
export async function writeEnvText(cwd, text) {
|
|
33
|
+
const target = localEnvPath(cwd);
|
|
34
|
+
const tmp = tmpNameFor(target);
|
|
35
|
+
try {
|
|
36
|
+
await fs.writeFile(tmp, text, { encoding: "utf8", mode: 0o600 });
|
|
37
|
+
await fs.rename(tmp, target);
|
|
38
|
+
} catch {
|
|
39
|
+
await fs.writeFile(target, text, { encoding: "utf8", mode: 0o600 });
|
|
40
|
+
await fs.rm(tmp, { force: true });
|
|
41
|
+
}
|
|
42
|
+
await restrictSecretPerms(target);
|
|
43
|
+
}
|
|
44
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/cli/env-load.js — Chargement du .env local dans l'environnement du
|
|
2
|
+
// processus : allowlist stricte (PLUGCTO_* + OLLAMA_HOST uniquement — jamais
|
|
3
|
+
// PATH & co) et sans écrasement (le shell gagne toujours). Les clés ne servent
|
|
4
|
+
// ensuite qu'aux en-têtes HTTP du provider, jamais au contexte IA.
|
|
5
|
+
|
|
6
|
+
import { parseEnvFile } from "./env-parse.js";
|
|
7
|
+
import { readLocalEnvText } from "./env-file.js";
|
|
8
|
+
|
|
9
|
+
/** @typedef {Record<string, string | undefined>} EnvLike */
|
|
10
|
+
|
|
11
|
+
// Allowlist Zero Trust : le .env PipCode n'injecte QUE ses variables + le
|
|
12
|
+
// host Ollama (anti-détournement d'environnement).
|
|
13
|
+
const ALLOWED = [/^PLUGCTO_/, /^OLLAMA_HOST$/];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Charge le .env du projet (cwd) dans env. Fail-safe : fichier absent → no-op
|
|
17
|
+
* silencieux ; contenu malformé → lignes invalides ignorées par le parseur.
|
|
18
|
+
* @param {string} [cwd] @param {EnvLike} [env] @returns {Promise<void>}
|
|
19
|
+
*/
|
|
20
|
+
export async function loadLocalEnv(cwd = process.cwd(), env = process.env) {
|
|
21
|
+
const text = await readLocalEnvText(cwd);
|
|
22
|
+
if (text === null) return;
|
|
23
|
+
for (const [key, value] of Object.entries(parseEnvFile(text))) {
|
|
24
|
+
if (!ALLOWED.some((re) => re.test(key))) continue;
|
|
25
|
+
if (!env[key]) env[key] = value;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/cli/env-parse.js — Parseur .env local (pur, zéro dépendance) : lecture
|
|
2
|
+
// KEY=VALUE stricte — identifiants valides uniquement, fail-safe sur lignes
|
|
3
|
+
// malformées, BOM/CRLF/guillemets/commentaires tolérés.
|
|
4
|
+
|
|
5
|
+
/** @typedef {Record<string, string>} EnvRecord */
|
|
6
|
+
|
|
7
|
+
// Identifiant .env valide : strict caractère shell (anti-injection de noms).
|
|
8
|
+
const KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
9
|
+
|
|
10
|
+
// Nettoie une valeur : retire guillemets encadrants et commentaire de fin.
|
|
11
|
+
/** @param {string} raw @returns {string} */
|
|
12
|
+
function cleanValue(raw) {
|
|
13
|
+
const v = raw.trim();
|
|
14
|
+
const quoted = /^"(.*?)"$/.exec(v) || /^'(.*?)'$/.exec(v);
|
|
15
|
+
if (quoted) return quoted[1];
|
|
16
|
+
const cut = v.indexOf(" #");
|
|
17
|
+
return (cut === -1 ? v : v.slice(0, cut)).trim();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parse un contenu .env en enregistrement { clé: valeur }. Lignes vides,
|
|
22
|
+
* commentaires, noms invalides et lignes malformées sont ignorés.
|
|
23
|
+
* @param {string} text @returns {EnvRecord}
|
|
24
|
+
*/
|
|
25
|
+
export function parseEnvFile(text) {
|
|
26
|
+
/** @type {EnvRecord} */ const out = {};
|
|
27
|
+
for (const line of text.replace(/^/, "").split(/\r?\n/)) {
|
|
28
|
+
const l = line.trim();
|
|
29
|
+
if (!l || l.startsWith("#")) continue;
|
|
30
|
+
const eq = l.indexOf("=");
|
|
31
|
+
if (eq < 1) continue;
|
|
32
|
+
const key = l.slice(0, eq).replace(/^export\s+/, "").trim();
|
|
33
|
+
if (!KEY_RE.test(key)) continue;
|
|
34
|
+
const value = cleanValue(l.slice(eq + 1));
|
|
35
|
+
if (value) out[key] = value;
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/cli/env-rebuild.js — Reconstruction du .env : remplace SEULEMENT
|
|
2
|
+
// l'espace de noms PLUGCTO_* par la nouvelle saisie (Entrée vide = choix
|
|
3
|
+
// explicite essai/local) ; les lignes tierces et commentaires de
|
|
4
|
+
// l'utilisateur sont préservées telles quelles, sans duplication de marqueur.
|
|
5
|
+
|
|
6
|
+
/** @typedef {Record<string, string>} EnvRecord */
|
|
7
|
+
|
|
8
|
+
// Marqueur du bloc généré par `config` (retiré à la reconstruction pour ne pas
|
|
9
|
+
// s'accumuler à chaque exécution).
|
|
10
|
+
const GEN_MARK = "# --- PipCode (npx pipcode config)";
|
|
11
|
+
const OLD_MARK = "# --- Plug" + " CTO (npx plugcto config)"; // ancien bloc (extension 0.12.x) : retiré à la reconstruction
|
|
12
|
+
|
|
13
|
+
// Vrai si la ligne porte une clé PLUGCTO_* remplaçable.
|
|
14
|
+
/** @param {string} line @returns {boolean} */
|
|
15
|
+
function isPlugctoKey(line) {
|
|
16
|
+
const l = line.trim();
|
|
17
|
+
const eq = l.indexOf("=");
|
|
18
|
+
if (eq < 1) return false;
|
|
19
|
+
const key = l.slice(0, eq).replace(/^export\s+/, "").trim();
|
|
20
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(key) && key.startsWith("PLUGCTO_");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Reconstruit le contenu .env à partir de l'existant et des nouvelles paires.
|
|
25
|
+
* @param {string} existingText @param {EnvRecord} pairs @returns {string}
|
|
26
|
+
*/
|
|
27
|
+
export function rebuildEnvText(existingText, pairs) {
|
|
28
|
+
const kept = existingText.replace(/^/, "").split(/\r?\n/)
|
|
29
|
+
.filter((line) => !isPlugctoKey(line) && !line.includes(GEN_MARK) && !line.includes(OLD_MARK));
|
|
30
|
+
const block = Object.entries(pairs).map(([k, v]) => `${k}=${v}`);
|
|
31
|
+
return [...kept, GEN_MARK + " — ne jamais committer ---", ...block, ""].join("\n");
|
|
32
|
+
}
|
|
33
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/cli/gitignore-env.js — Garde anti-commit : `.env` (secrets locaux) doit
|
|
2
|
+
// toujours figurer dans le .gitignore du projet ; ajout automatique sinon.
|
|
3
|
+
// Détection : toute ligne non-niée désignant `.env` ou `.env.*` suffit.
|
|
4
|
+
|
|
5
|
+
import fs from "node:fs/promises";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
/** @typedef {Error & { code?: string }} NodeErr */
|
|
9
|
+
|
|
10
|
+
const HEADER = "# PipCode — secrets locaux (.env) : ne jamais committer";
|
|
11
|
+
|
|
12
|
+
// Vrai si le contenu ignore déjà `.env` (motif `.env` exact ou préfixe
|
|
13
|
+
// `.env.*` ; les négations `!.env` ne comptent pas).
|
|
14
|
+
/** @param {string} text @returns {boolean} */
|
|
15
|
+
export function gitignoreHasEnv(text) {
|
|
16
|
+
return text.split(/\r?\n/).some((line) => {
|
|
17
|
+
const t = line.trim();
|
|
18
|
+
return !t.startsWith("!") && (t === ".env" || t.startsWith(".env."));
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Ajoute `.env` au .gitignore du projet (créé si absent, inchangé si déjà
|
|
23
|
+
// présent). Best-effort : un .gitignore non lisible/inscriptible est signalé
|
|
24
|
+
// sans faire échouer `config`.
|
|
25
|
+
/** @param {string} [cwd] @returns {Promise<void>} */
|
|
26
|
+
export async function ensureEnvIgnored(cwd = process.cwd()) {
|
|
27
|
+
const gi = path.join(cwd, ".gitignore");
|
|
28
|
+
/** @type {string} */ let text = "";
|
|
29
|
+
try {
|
|
30
|
+
text = await fs.readFile(gi, "utf8");
|
|
31
|
+
} catch (e) {
|
|
32
|
+
const err = /** @type {NodeErr} */ (e);
|
|
33
|
+
if (err.code !== "ENOENT") {
|
|
34
|
+
console.error(` ⚠️ .gitignore illisible (${err.message}) : vérifiez que .env est ignoré manuellement.`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (gitignoreHasEnv(text)) return;
|
|
39
|
+
const glue = text === "" || text.endsWith("\n") ? "" : "\n";
|
|
40
|
+
try {
|
|
41
|
+
await fs.writeFile(gi, text + glue + `${HEADER}\n.env\n`, "utf8");
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.error(` ⚠️ Impossible d'ajouter .env au .gitignore (${/** @type {NodeErr} */ (e).message}) : faites-le manuellement.`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// MODULE_COMPLETE
|
package/src/cli/help.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/cli/help.js — Textes d'aide CLI du hub d'entrée (setup / auto / --help).
|
|
2
|
+
// Extraits du dispatcher bin/setup.js pour garder l'entry point <50 lignes.
|
|
3
|
+
|
|
4
|
+
// Aide du Mode Auto : plan / run / resume (orchestrateur d'autonomie, BYOK).
|
|
5
|
+
export function printAutoHelp() {
|
|
6
|
+
console.log("PipCode — Mode Auto (orchestrateur d'autonomie, BYOK)\n");
|
|
7
|
+
console.log("Usage :");
|
|
8
|
+
console.log(' npx pipcode auto plan --need "…" --project ./monrepo Génère le roadmap + estimation (dry-run).');
|
|
9
|
+
console.log(" npx pipcode auto run --project ./monrepo [--yes] Exécute (consentement Y/n requis).");
|
|
10
|
+
console.log(" npx pipcode auto resume --project ./monrepo Reprend après crash/arrêt.");
|
|
11
|
+
console.log(' npx pipcode auto ask --need "…" --project ./monrepo Conversation pure : posez votre question à votre IA (aucune exécution).');
|
|
12
|
+
console.log(" Option --require vision,tools,media_generation : vérifie AVANT l'appel que le modèle sait faire la tâche (sinon arrêt, 0 token).");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Aide générale : setup / auto / --help.
|
|
16
|
+
export function printTopHelp() {
|
|
17
|
+
console.log("PipCode — auto-configuration MCP (Zero-Click)\n");
|
|
18
|
+
console.log("Usage :");
|
|
19
|
+
console.log(" npx pipcode setup Configure le serveur MCP PipCode dans les IDE détectés.");
|
|
20
|
+
console.log(" npx pipcode setup --key <CLÉ> Enregistre la clé de licence Pro (Polar.sh) —");
|
|
21
|
+
console.log(" utilisable par un agent IA, sans interaction.");
|
|
22
|
+
console.log(" npx pipcode teardown Retire PipCode de vos outils (seule l'entrée PipCode est supprimée).");
|
|
23
|
+
console.log(" npx pipcode config Saisie interactive des clés (licence + BYOK) → .env local du");
|
|
24
|
+
console.log(" projet, hors Git (Entrée = essai 5 jours / mode local).");
|
|
25
|
+
console.log(" npx pipcode auto Mode Auto : plan / run / resume / ask (orchestrateur d'autonomie + conversation).");
|
|
26
|
+
console.log(" npx pipcode --help Affiche cette aide.");
|
|
27
|
+
}
|
|
28
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/cli/ide-entries.js — Candidats de config IDE (entrées brutes avant enrich).
|
|
2
|
+
// coreIdeEntries : Cursor, Claude Desktop, Claude Code, Windsurf, Gemini CLI, Hermes,
|
|
3
|
+
// OpenClaw (schéma mcpServers) + Codex (TOML, partagé avec l'app ChatGPT desktop).
|
|
4
|
+
// uiIdeEntries : VS Code natif (servers), VS 2022 (Windows, servers), Continue
|
|
5
|
+
// (mcpServers), Zed (context_servers). Chaque entrée = { id, name, path, schema? }.
|
|
6
|
+
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
|
|
9
|
+
/** @typedef {{ id: string, name: string, path: string, schema?: "mcpServers" | "servers" | "context_servers" | "toml" }} IdeConfigCandidate */
|
|
10
|
+
|
|
11
|
+
// Entrées cœur (toutes plateformes ; JSON mcpServers sauf Codex = TOML).
|
|
12
|
+
/** @param {string} home @param {string} configRoot @returns {IdeConfigCandidate[]} */
|
|
13
|
+
export function coreIdeEntries(home, configRoot) {
|
|
14
|
+
return [
|
|
15
|
+
{ id: "cursor", name: "Cursor", path: path.join(home, ".cursor", "mcp.json") },
|
|
16
|
+
{ id: "claude-desktop", name: "Claude Desktop", path: path.join(configRoot, "Claude", "claude_desktop_config.json") },
|
|
17
|
+
{ id: "claude-code", name: "Claude Code", path: path.join(home, ".claude.json") },
|
|
18
|
+
{ id: "windsurf", name: "Windsurf", path: path.join(home, ".codeium", "windsurf", "mcp_config.json") },
|
|
19
|
+
{ id: "gemini-cli", name: "Gemini CLI", path: path.join(home, ".gemini", "settings.json") },
|
|
20
|
+
{ id: "codex", name: "Codex / ChatGPT", path: path.join(home, ".codex", "config.toml"), schema: "toml" },
|
|
21
|
+
{ id: "hermes", name: "Hermes (CLI)", path: path.join(home, ".hermes", "config.json") },
|
|
22
|
+
{ id: "openclaw", name: "OpenClaw (CLI)", path: path.join(home, ".openclaw", "mcp.json") },
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Entrées UI : VS Code natif (MCP natif 1.106+, schéma servers), VS 2022 (Windows
|
|
27
|
+
// only, schéma servers avec type:stdio), Continue (VS Code + JetBrains, mcpServers),
|
|
28
|
+
// Zed (context_servers ; %APPDATA%\Zed sous Windows, ~/.config/zed ailleurs).
|
|
29
|
+
/** @param {string} osName @param {string} codeUser @param {string} home @param {string} [configRoot] @returns {IdeConfigCandidate[]} */
|
|
30
|
+
export function uiIdeEntries(osName, codeUser, home, configRoot = path.join(home, ".config")) {
|
|
31
|
+
const zedDir = osName === "windows" ? path.join(configRoot, "Zed") : path.join(osName === "macos" ? path.join(home, ".config") : configRoot, "zed");
|
|
32
|
+
/** @type {IdeConfigCandidate[]} */ const entries = [
|
|
33
|
+
{ id: "vscode", name: "VS Code (natif)", path: path.join(codeUser, "mcp.json"), schema: "servers" },
|
|
34
|
+
{ id: "continue", name: "Continue (VS Code + JetBrains)", path: path.join(home, ".continue", "config.json") },
|
|
35
|
+
{ id: "zed", name: "Zed", path: path.join(zedDir, "settings.json"), schema: "context_servers" },
|
|
36
|
+
];
|
|
37
|
+
if (osName === "windows") {
|
|
38
|
+
entries.push({ id: "vs-2022", name: "Visual Studio 2022", path: path.join(home, ".mcp.json"), schema: "servers" });
|
|
39
|
+
}
|
|
40
|
+
return entries;
|
|
41
|
+
}
|
|
42
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/cli/ide-paths.js — Localisation des fichiers de config IDE (orchestrateur).
|
|
2
|
+
// Combine les entrées cœur + UI + JetBrains, puis enrichit chaque candidat avec
|
|
3
|
+
// parentExists (dossier parent présent = IDE probablement installé ; pour un fichier posé DIRECTEMENT dans le dossier personnel,
|
|
4
|
+
// le dossier existe toujours : seul le fichier déjà présent prouve l'outil — PipCode ne crée pas ~/.claude.json ni ~/.mcp.json pour un outil absent) et configExists
|
|
5
|
+
// (fichier JSON déjà présent). `osName`/`env`/`home` injectables pour les tests.
|
|
6
|
+
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
|
+
import { detectOS } from "./detect-os.js";
|
|
11
|
+
import { ideConfigRoot } from "./ide-roots.js";
|
|
12
|
+
import { coreIdeEntries, uiIdeEntries } from "./ide-entries.js";
|
|
13
|
+
import { jetbrainsCandidates } from "./jetbrains.js";
|
|
14
|
+
|
|
15
|
+
/** @typedef {{ id: string, name: string, path: string, parentExists: boolean, configExists: boolean, schema?: "mcpServers" | "servers" | "context_servers" | "toml" }} IdeConfigEntry */
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} [osName]
|
|
19
|
+
* @param {Record<string, string | undefined>} [env]
|
|
20
|
+
* @param {string} [home]
|
|
21
|
+
* @returns {IdeConfigEntry[]}
|
|
22
|
+
*/
|
|
23
|
+
export function ideConfigPaths(osName = detectOS(), env = process.env, home = os.homedir()) {
|
|
24
|
+
const { configRoot, codeUser } = ideConfigRoot(osName, env || {}, home);
|
|
25
|
+
/** @type {Array<{ id: string, name: string, path: string, schema?: "mcpServers" | "servers" | "context_servers" | "toml" }>} */
|
|
26
|
+
const entries = [
|
|
27
|
+
...coreIdeEntries(home, configRoot),
|
|
28
|
+
...uiIdeEntries(osName, codeUser, home, configRoot),
|
|
29
|
+
...jetbrainsCandidates(osName, env || {}, home, configRoot),
|
|
30
|
+
];
|
|
31
|
+
return entries.map((e) => ({
|
|
32
|
+
...e,
|
|
33
|
+
parentExists: existsSync(path.dirname(e.path)) && (path.resolve(path.dirname(e.path)) !== path.resolve(home) || existsSync(e.path)),
|
|
34
|
+
configExists: existsSync(e.path),
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/cli/ide-roots.js — Racines de config IDE par OS (bornées sous home).
|
|
2
|
+
// Un override env (APPDATA / XDG_CONFIG_HOME) hors home est ignoré : empêche de
|
|
3
|
+
// rediriger l'écriture des clés API hors du profil utilisateur.
|
|
4
|
+
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { boundRoot, DEFAULT_XDG, DEFAULT_APPDATA } from "../fs/config-path.js";
|
|
7
|
+
|
|
8
|
+
/** @typedef {{ configRoot: string, codeUser: string, defaultXdg: string }} IdeRoots */
|
|
9
|
+
|
|
10
|
+
// Calcule les racines config par OS + le dossier User de VS Code (varie par OS).
|
|
11
|
+
/** @param {string} osName @param {Record<string, string | undefined>} env @param {string} home @returns {IdeRoots} */
|
|
12
|
+
export function ideConfigRoot(osName, env, home) {
|
|
13
|
+
const defaultXdg = DEFAULT_XDG(home);
|
|
14
|
+
const defaultAppData = DEFAULT_APPDATA(home);
|
|
15
|
+
const configRoot = osName === "macos"
|
|
16
|
+
? path.join(home, "Library", "Application Support")
|
|
17
|
+
: osName === "windows"
|
|
18
|
+
? boundRoot(env.APPDATA, home, defaultAppData)
|
|
19
|
+
: boundRoot(env.XDG_CONFIG_HOME, home, defaultXdg);
|
|
20
|
+
const codeUser = osName === "macos" || osName === "windows"
|
|
21
|
+
? path.join(configRoot, "Code", "User")
|
|
22
|
+
: path.join(boundRoot(env.XDG_CONFIG_HOME, home, defaultXdg), "Code", "User");
|
|
23
|
+
return { configRoot, codeUser, defaultXdg };
|
|
24
|
+
}
|
|
25
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/cli/interactive.js — Flux interactif TTY (collectInteractive) : authentification seule (token + e-mail centralisés). Retourne { licenseKey, email }. Aucune clé d'IA
|
|
2
|
+
// n'est demandée. Dépendances injectées pour les tests.
|
|
3
|
+
|
|
4
|
+
import { makePrompts } from "./prompts.js";
|
|
5
|
+
import { collectAuth } from "./auth-interactive.js";
|
|
6
|
+
import { authViaBrowser } from "../net/auth-browser.js";
|
|
7
|
+
import { authViaEmail } from "../auth/auth-email.js";
|
|
8
|
+
|
|
9
|
+
/** @typedef {{ licenseKey: string, email: string }} CollectedInteractive */
|
|
10
|
+
/** @typedef {{ installedAt?: string, clientId?: string, licenseKey?: string, email?: string, trialExpiredNotified?: boolean }} SharedConfig */
|
|
11
|
+
/** @typedef {{ key: string, email: string, source: "browser", error?: string }} AuthBrowserResult */
|
|
12
|
+
/** @typedef {{ found: boolean, tier: string | null, email: string, source: "email", error?: string }} AuthEmailResult */
|
|
13
|
+
/** @typedef {{ browser?: () => Promise<AuthBrowserResult>, emailLookup?: (email: string) => Promise<AuthEmailResult>, existingConfig?: SharedConfig }} CollectInteractiveOpts */
|
|
14
|
+
|
|
15
|
+
import { Interface } from "node:readline/promises";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {Interface} rl
|
|
19
|
+
* @param {CollectInteractiveOpts} [opts]
|
|
20
|
+
* @returns {Promise<CollectedInteractive>}
|
|
21
|
+
*/
|
|
22
|
+
export async function collectInteractive(rl, { browser = authViaBrowser, emailLookup = authViaEmail, existingConfig = {} } = {}) {
|
|
23
|
+
const { licenseKey, email } = await collectAuth(makePrompts(rl), { browser, emailLookup, existingConfig });
|
|
24
|
+
return { licenseKey, email };
|
|
25
|
+
}
|
|
26
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/cli/jetbrains.js — Énumération des produits JetBrains installés.
|
|
2
|
+
// Scanne le dossier JetBrains/<Produit>/ et émet une entrée mcp.json par sous-dossier
|
|
3
|
+
// produit trouvé. Retourne [] si le dossier n'existe pas. Filtré : sous-dossiers
|
|
4
|
+
// only, pas de hidden. id normalisé "jetbrains-<produit>".
|
|
5
|
+
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
8
|
+
import { boundRoot, DEFAULT_XDG, DEFAULT_APPDATA } from "../fs/config-path.js";
|
|
9
|
+
|
|
10
|
+
/** @typedef {{ id: string, name: string, path: string, schema?: "mcpServers" | "servers" }} IdeConfigCandidate */
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} osName
|
|
14
|
+
* @param {Record<string, string | undefined>} env
|
|
15
|
+
* @param {string} home
|
|
16
|
+
* @param {string} _configRoot
|
|
17
|
+
* @returns {IdeConfigCandidate[]}
|
|
18
|
+
*/
|
|
19
|
+
export function jetbrainsCandidates(osName, env, home, _configRoot) {
|
|
20
|
+
const jbRoot = osName === "macos"
|
|
21
|
+
? path.join(home, "Library", "Application Support", "JetBrains")
|
|
22
|
+
: osName === "windows"
|
|
23
|
+
? path.join(boundRoot(env.APPDATA, home, DEFAULT_APPDATA(home)), "JetBrains")
|
|
24
|
+
: path.join(boundRoot(env.XDG_CONFIG_HOME, home, DEFAULT_XDG(home)), "JetBrains");
|
|
25
|
+
if (!existsSync(jbRoot)) return [];
|
|
26
|
+
/** @type {string[]} */ let dirs = [];
|
|
27
|
+
try {
|
|
28
|
+
dirs = readdirSync(jbRoot).filter((d) => {
|
|
29
|
+
if (d.startsWith(".")) return false;
|
|
30
|
+
try { return statSync(path.join(jbRoot, d)).isDirectory(); } catch { return false; }
|
|
31
|
+
});
|
|
32
|
+
} catch {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
return dirs.map((d) => {
|
|
36
|
+
const slug = d.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
37
|
+
return { id: `jetbrains-${slug}`, name: `JetBrains — ${d}`, path: path.join(jbRoot, d, "mcp.json") };
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// src/cli/legacy-entry.js — Ancien nom de l'entrée MCP (« plugcto » puis « vcto », avant le passage à « pipcode ») : à la réinstallation il est remplacé, à la désinstallation il est retiré aussi.
|
|
2
|
+
// Seule une entrée qui est bien la nôtre (variables PLUGCTO_* ou point d'entrée index.js) est touchée : un serveur d'un autre nom n'est jamais supprimé.
|
|
3
|
+
|
|
4
|
+
export const LEGACY_SERVER_KEYS = ["plugcto", "vcto"];
|
|
5
|
+
|
|
6
|
+
/** @param {unknown} entry @returns {boolean} */
|
|
7
|
+
export function isOurLegacyEntry(entry) {
|
|
8
|
+
if (!entry || typeof entry !== "object") return false;
|
|
9
|
+
const e = /** @type {{ env?: Record<string, unknown>, args?: unknown }} */ (entry);
|
|
10
|
+
return Object.keys(e.env || {}).some((k) => k.startsWith("PLUGCTO_")) || (Array.isArray(e.args) && String(e.args[0] || "").endsWith("index.js"));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** @param {Record<string, unknown>} bucket @returns {void} retire l'ancienne entrée du dictionnaire de serveurs si elle est la nôtre */
|
|
14
|
+
export function dropLegacyEntry(bucket) {
|
|
15
|
+
for (const k of LEGACY_SERVER_KEYS) if (isOurLegacyEntry(bucket[k])) delete bucket[k];
|
|
16
|
+
}
|
|
17
|
+
// MODULE_COMPLETE
|
package/src/cli/main.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/cli/main.js — Orchestrateur du setup (`npx pipcode setup`).
|
|
2
|
+
// Détection OS → bannière → collecte → centralisation → sélection cibles → application.
|
|
3
|
+
// Entry point : aucune logique métier, coordonne seulement les micro-modules.
|
|
4
|
+
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { stdin } from "node:process";
|
|
7
|
+
import { detectOS } from "./detect-os.js";
|
|
8
|
+
import { ideConfigPaths } from "./ide-paths.js";
|
|
9
|
+
import { printBanner } from "./banner.js";
|
|
10
|
+
import { collectForSetup } from "./collect-step.js";
|
|
11
|
+
import { centralizeConfig } from "./centralize.js";
|
|
12
|
+
import { selectTargets } from "./target-select.js";
|
|
13
|
+
import { applyToTargets } from "./apply-loop.js";
|
|
14
|
+
import { WELCOME_MESSAGE } from "./welcome.js";
|
|
15
|
+
import { detectPackageManager } from "../core/pkg-manager.js";
|
|
16
|
+
import { applyKeyToEnv } from "./setup-args.js";
|
|
17
|
+
|
|
18
|
+
// Chemin absolu vers index.js (racine projet).
|
|
19
|
+
const INDEX_JS = path.resolve(import.meta.dirname, "..", "..", "index.js");
|
|
20
|
+
|
|
21
|
+
// Entry point du setup : orchestration des 4 étapes.
|
|
22
|
+
export async function main() {
|
|
23
|
+
// `--key <LICENSE_KEY>` : enregistrement direct par un agent IA (non-TTY).
|
|
24
|
+
// slice(2) tolère `setup --key K` comme `--key K` (sous-commande par défaut).
|
|
25
|
+
applyKeyToEnv(process.argv.slice(2), process.env);
|
|
26
|
+
const osName = detectOS();
|
|
27
|
+
const candidates = ideConfigPaths(osName);
|
|
28
|
+
const detected = candidates.filter((c) => c.parentExists);
|
|
29
|
+
printBanner(osName, candidates);
|
|
30
|
+
const pm = detectPackageManager();
|
|
31
|
+
console.log(` 📦 Gestionnaire détecté : ${pm.label} — routines 100 % Node.js pur, indépendantes de npm/pnpm/yarn/bun.`);
|
|
32
|
+
const isTTY = stdin.isTTY;
|
|
33
|
+
const collected = await collectForSetup(isTTY);
|
|
34
|
+
await centralizeConfig(collected);
|
|
35
|
+
const targets = await selectTargets(detected, candidates, isTTY);
|
|
36
|
+
if (!targets) { if (!isTTY) process.exit(0); return; }
|
|
37
|
+
await applyToTargets(targets, collected, INDEX_JS);
|
|
38
|
+
console.log("\n ✅ Configuration terminée. Redémarrez l'IDE pour activer le serveur MCP PipCode.");
|
|
39
|
+
console.log(`\n ${WELCOME_MESSAGE}`); // préenregistré : zéro token (exigence produit)
|
|
40
|
+
if (!isTTY) process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/cli/memory-cli.js — `memory index --project <chemin>` : carte exacte du projet (gratuit, aucun LLM, aucun réseau, hors
|
|
2
|
+
// ligne). Sortie : UNE ligne JSON. La mémoire rédigée se construit au fil des messages du chat (balises de la réponse).
|
|
3
|
+
|
|
4
|
+
import { parseAutoFlags, resolveProjectPath } from "./auto-flags.js";
|
|
5
|
+
import { indexProject } from "../project-memory/memory-build.js";
|
|
6
|
+
|
|
7
|
+
/** @param {string} sub @param {string[]} argv @returns {Promise<void>} */
|
|
8
|
+
export async function runMemoryCli(sub, argv) {
|
|
9
|
+
const project = resolveProjectPath(parseAutoFlags(argv));
|
|
10
|
+
if (sub !== "index") { console.error("Sous-action memory inconnue. Essayez : index"); process.exitCode = 2; return; }
|
|
11
|
+
const { index, diff } = await indexProject(project);
|
|
12
|
+
const bytes = Object.values(index.files).reduce((n, e) => n + e.bytes, 0); // taille des sources : sert à estimer le coût d'une analyse
|
|
13
|
+
console.log(JSON.stringify({ files: Object.keys(index.files).length, bytes, ...diff }));
|
|
14
|
+
}
|
|
15
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/cli/prompts.js — Helpers de saisie readline (flux interactif TTY).
|
|
2
|
+
// ask : saisie simple trimée. askDefault : valeur par défaut si Entrée.
|
|
3
|
+
// askRequired : boucle jusqu'à valeur non vide (valeur requise).
|
|
4
|
+
|
|
5
|
+
/** @typedef {{ ask: (q: string) => Promise<string>, askDefault: (q: string, def: string | undefined) => Promise<string>, askRequired: (q: string) => Promise<string> }} Prompts */
|
|
6
|
+
|
|
7
|
+
// Fabrique les helpers de saisie liés à une interface readline donnée.
|
|
8
|
+
/** @param {import("node:readline/promises").Interface} rl @returns {Prompts} */
|
|
9
|
+
export function makePrompts(rl) {
|
|
10
|
+
const ask = async (/** @type {string} */ q) => (await rl.question(q)).trim();
|
|
11
|
+
const askDefault = async (/** @type {string} */ q, /** @type {string | undefined} */ def) => {
|
|
12
|
+
const a = (await rl.question(q)).trim();
|
|
13
|
+
return /** @type {string} */ (a || def);
|
|
14
|
+
};
|
|
15
|
+
const askRequired = async (/** @type {string} */ q) => {
|
|
16
|
+
while (true) {
|
|
17
|
+
const a = (await rl.question(q)).trim();
|
|
18
|
+
if (a) return a;
|
|
19
|
+
console.error(" ⚠️ Valeur requise. Réessayez.");
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
return { ask, askDefault, askRequired };
|
|
23
|
+
}
|
|
24
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/cli/server-entry.js — Construction de l'entrée serveur MCP PipCode + merge.
|
|
2
|
+
// buildServerEntry : { command, args, env } pointant vers l'exécutable local
|
|
3
|
+
// index.js. mergeConfig : injection idempotent (préservation STRICTE des autres
|
|
4
|
+
// serveurs MCP et de toutes les autres clés du fichier). Retourne un NOUVEL objet.
|
|
5
|
+
|
|
6
|
+
import { dropLegacyEntry } from "./legacy-entry.js";
|
|
7
|
+
|
|
8
|
+
export const SERVER_KEY = "pipcode"; // nom sous lequel les agents voient PipCode : /mcp__pipcode__licence dans Claude Code
|
|
9
|
+
|
|
10
|
+
/** @typedef {{ command: string, args: string[], env: Record<string, string> }} ServerEntry */
|
|
11
|
+
|
|
12
|
+
// `envVars` = objet plat des variables BYOK injectées dans l'env du process MCP.
|
|
13
|
+
/** @param {string} indexAbsPath @param {Record<string, string>} envVars @returns {ServerEntry} */
|
|
14
|
+
export function buildServerEntry(indexAbsPath, envVars) {
|
|
15
|
+
return { command: "node", args: [indexAbsPath], env: { ...envVars } };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// `schema` : "mcpServers" (défaut, { command, args, env }), "servers" (VS Code natif /
|
|
19
|
+
// VS 2022, entrée stdio avec "type": "stdio" requis en tête) ou "context_servers"
|
|
20
|
+
// (Zed, même forme { command, args, env } sous une autre clé racine).
|
|
21
|
+
/**
|
|
22
|
+
* @param {Record<string, unknown>} config
|
|
23
|
+
* @param {ServerEntry} serverEntry
|
|
24
|
+
* @param {string} [key]
|
|
25
|
+
* @param {"mcpServers" | "servers" | "context_servers"} [schema]
|
|
26
|
+
* @returns {Record<string, unknown>}
|
|
27
|
+
*/
|
|
28
|
+
export function mergeConfig(config, serverEntry, key = SERVER_KEY, schema = "mcpServers") {
|
|
29
|
+
const next = (config && typeof config === "object" && !Array.isArray(config))
|
|
30
|
+
? { .../** @type {Record<string, unknown>} */ (config) }
|
|
31
|
+
: {};
|
|
32
|
+
const rootKey = schema === "servers" || schema === "context_servers" ? schema : "mcpServers";
|
|
33
|
+
const bucket = (next[rootKey] && typeof next[rootKey] === "object" && !Array.isArray(next[rootKey]))
|
|
34
|
+
? { .../** @type {Record<string, unknown>} */ (next[rootKey]) }
|
|
35
|
+
: {};
|
|
36
|
+
if (key === SERVER_KEY) dropLegacyEntry(bucket); // les anciennes entrées « plugcto » / « vcto » sont remplacées, jamais doublées
|
|
37
|
+
// Schéma "servers" : "type": "stdio" requis en tête (on ne mute pas serverEntry).
|
|
38
|
+
bucket[key] = schema === "servers" ? { type: "stdio", ...serverEntry } : serverEntry;
|
|
39
|
+
next[rootKey] = bucket;
|
|
40
|
+
return next;
|
|
41
|
+
}
|
|
42
|
+
// MODULE_COMPLETE
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/cli/setup-args.js — Argument `--key <LICENSE_KEY>` de `npx pipcode setup`.
|
|
2
|
+
// Objectif : un agent IA peut enregistrer la clé de licence directement lors de
|
|
3
|
+
// l'installation CLI (non-TTY). Zero Trust : format strictement validé, rejet
|
|
4
|
+
// propre si malformée ; l'injection env n'écrase JAMAIS une valeur existante.
|
|
5
|
+
|
|
6
|
+
/** @typedef {Record<string, string | undefined>} EnvLike */
|
|
7
|
+
|
|
8
|
+
// Format attendu d'une clé de licence Polar.sh (alphanum, tirets/underscores).
|
|
9
|
+
// Exporté : partagé avec config-pairs.js (zéro duplication de la règle).
|
|
10
|
+
export const KEY_RE = /^[A-Za-z0-9_-]{8,128}$/;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Extrait la valeur de `--key <K>` ou `--key=<K>` (absent → null). Malformée → Error.
|
|
14
|
+
* @param {string[]} argv Arguments SANS le binaire (ex: ["setup", "--key", "X"]).
|
|
15
|
+
* @returns {string | null}
|
|
16
|
+
*/
|
|
17
|
+
export function extractKeyArg(argv) {
|
|
18
|
+
const i = argv.findIndex((a) => a === "--key" || a.startsWith("--key="));
|
|
19
|
+
if (i === -1) return null;
|
|
20
|
+
const raw = argv[i] === "--key" ? (i + 1 < argv.length ? argv[i + 1] : "") : argv[i].slice("--key=".length);
|
|
21
|
+
const key = String(raw || "").trim();
|
|
22
|
+
if (!KEY_RE.test(key)) {
|
|
23
|
+
throw new Error("Argument --key invalide : clé de licence malformée (8-128 caractères alphanumériques, tirets ou underscores).");
|
|
24
|
+
}
|
|
25
|
+
return key;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Injecte la clé dans env.PLUGCTO_LICENSE_KEY (sans écraser une valeur existante).
|
|
30
|
+
* @param {string[]} argv
|
|
31
|
+
* @param {EnvLike} env
|
|
32
|
+
* @returns {string | null}
|
|
33
|
+
*/
|
|
34
|
+
export function applyKeyToEnv(argv, env) {
|
|
35
|
+
const key = extractKeyArg(argv);
|
|
36
|
+
if (key && !env.PLUGCTO_LICENSE_KEY) env.PLUGCTO_LICENSE_KEY = key;
|
|
37
|
+
return key;
|
|
38
|
+
}
|
|
39
|
+
// MODULE_COMPLETE
|