opensip-cli 0.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 +202 -0
- package/NOTICE +8 -0
- package/README.md +51 -0
- package/dist/api.d.ts +17 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +16 -0
- package/dist/api.js.map +1 -0
- package/dist/bootstrap/admit-tool-package.d.ts +117 -0
- package/dist/bootstrap/admit-tool-package.d.ts.map +1 -0
- package/dist/bootstrap/admit-tool-package.js +170 -0
- package/dist/bootstrap/admit-tool-package.js.map +1 -0
- package/dist/bootstrap/baseline-seams.d.ts +30 -0
- package/dist/bootstrap/baseline-seams.d.ts.map +1 -0
- package/dist/bootstrap/baseline-seams.js +156 -0
- package/dist/bootstrap/baseline-seams.js.map +1 -0
- package/dist/bootstrap/bootstrap-error.d.ts +41 -0
- package/dist/bootstrap/bootstrap-error.d.ts.map +1 -0
- package/dist/bootstrap/bootstrap-error.js +33 -0
- package/dist/bootstrap/bootstrap-error.js.map +1 -0
- package/dist/bootstrap/build-command-registration-input.d.ts +34 -0
- package/dist/bootstrap/build-command-registration-input.d.ts.map +1 -0
- package/dist/bootstrap/build-command-registration-input.js +73 -0
- package/dist/bootstrap/build-command-registration-input.js.map +1 -0
- package/dist/bootstrap/build-per-run-scope.d.ts +62 -0
- package/dist/bootstrap/build-per-run-scope.d.ts.map +1 -0
- package/dist/bootstrap/build-per-run-scope.js +152 -0
- package/dist/bootstrap/build-per-run-scope.js.map +1 -0
- package/dist/bootstrap/build-targets.d.ts +42 -0
- package/dist/bootstrap/build-targets.d.ts.map +1 -0
- package/dist/bootstrap/build-targets.js +117 -0
- package/dist/bootstrap/build-targets.js.map +1 -0
- package/dist/bootstrap/cli-defaults.d.ts +35 -0
- package/dist/bootstrap/cli-defaults.d.ts.map +1 -0
- package/dist/bootstrap/cli-defaults.js +65 -0
- package/dist/bootstrap/cli-defaults.js.map +1 -0
- package/dist/bootstrap/config-and-capabilities.d.ts +74 -0
- package/dist/bootstrap/config-and-capabilities.d.ts.map +1 -0
- package/dist/bootstrap/config-and-capabilities.js +224 -0
- package/dist/bootstrap/config-and-capabilities.js.map +1 -0
- package/dist/bootstrap/deliver-envelope.d.ts +80 -0
- package/dist/bootstrap/deliver-envelope.d.ts.map +1 -0
- package/dist/bootstrap/deliver-envelope.js +195 -0
- package/dist/bootstrap/deliver-envelope.js.map +1 -0
- package/dist/bootstrap/egress-plane.d.ts +22 -0
- package/dist/bootstrap/egress-plane.d.ts.map +1 -0
- package/dist/bootstrap/egress-plane.js +37 -0
- package/dist/bootstrap/egress-plane.js.map +1 -0
- package/dist/bootstrap/host-planes.d.ts +28 -0
- package/dist/bootstrap/host-planes.d.ts.map +1 -0
- package/dist/bootstrap/host-planes.js +152 -0
- package/dist/bootstrap/host-planes.js.map +1 -0
- package/dist/bootstrap/index.d.ts +76 -0
- package/dist/bootstrap/index.d.ts.map +1 -0
- package/dist/bootstrap/index.js +109 -0
- package/dist/bootstrap/index.js.map +1 -0
- package/dist/bootstrap/live-plane.d.ts +51 -0
- package/dist/bootstrap/live-plane.d.ts.map +1 -0
- package/dist/bootstrap/live-plane.js +72 -0
- package/dist/bootstrap/live-plane.js.map +1 -0
- package/dist/bootstrap/load-tool-capabilities.d.ts +42 -0
- package/dist/bootstrap/load-tool-capabilities.d.ts.map +1 -0
- package/dist/bootstrap/load-tool-capabilities.js +76 -0
- package/dist/bootstrap/load-tool-capabilities.js.map +1 -0
- package/dist/bootstrap/output-plane.d.ts +37 -0
- package/dist/bootstrap/output-plane.d.ts.map +1 -0
- package/dist/bootstrap/output-plane.js +114 -0
- package/dist/bootstrap/output-plane.js.map +1 -0
- package/dist/bootstrap/owning-tool-init.d.ts +32 -0
- package/dist/bootstrap/owning-tool-init.d.ts.map +1 -0
- package/dist/bootstrap/owning-tool-init.js +69 -0
- package/dist/bootstrap/owning-tool-init.js.map +1 -0
- package/dist/bootstrap/pre-action-guards.d.ts +44 -0
- package/dist/bootstrap/pre-action-guards.d.ts.map +1 -0
- package/dist/bootstrap/pre-action-guards.js +136 -0
- package/dist/bootstrap/pre-action-guards.js.map +1 -0
- package/dist/bootstrap/pre-action-hook.d.ts +68 -0
- package/dist/bootstrap/pre-action-hook.d.ts.map +1 -0
- package/dist/bootstrap/pre-action-hook.js +289 -0
- package/dist/bootstrap/pre-action-hook.js.map +1 -0
- package/dist/bootstrap/pre-action-messages.d.ts +32 -0
- package/dist/bootstrap/pre-action-messages.d.ts.map +1 -0
- package/dist/bootstrap/pre-action-messages.js +49 -0
- package/dist/bootstrap/pre-action-messages.js.map +1 -0
- package/dist/bootstrap/process-idempotency.d.ts +17 -0
- package/dist/bootstrap/process-idempotency.d.ts.map +1 -0
- package/dist/bootstrap/process-idempotency.js +20 -0
- package/dist/bootstrap/process-idempotency.js.map +1 -0
- package/dist/bootstrap/register-language-adapters.d.ts +23 -0
- package/dist/bootstrap/register-language-adapters.d.ts.map +1 -0
- package/dist/bootstrap/register-language-adapters.js +35 -0
- package/dist/bootstrap/register-language-adapters.js.map +1 -0
- package/dist/bootstrap/register-tools.d.ts +228 -0
- package/dist/bootstrap/register-tools.d.ts.map +1 -0
- package/dist/bootstrap/register-tools.js +696 -0
- package/dist/bootstrap/register-tools.js.map +1 -0
- package/dist/bootstrap/render.d.ts +27 -0
- package/dist/bootstrap/render.d.ts.map +1 -0
- package/dist/bootstrap/render.js +53 -0
- package/dist/bootstrap/render.js.map +1 -0
- package/dist/bootstrap/report.d.ts +34 -0
- package/dist/bootstrap/report.d.ts.map +1 -0
- package/dist/bootstrap/report.js +47 -0
- package/dist/bootstrap/report.js.map +1 -0
- package/dist/bootstrap/run-plane.d.ts +105 -0
- package/dist/bootstrap/run-plane.d.ts.map +1 -0
- package/dist/bootstrap/run-plane.js +190 -0
- package/dist/bootstrap/run-plane.js.map +1 -0
- package/dist/bootstrap/scope-access.d.ts +68 -0
- package/dist/bootstrap/scope-access.d.ts.map +1 -0
- package/dist/bootstrap/scope-access.js +115 -0
- package/dist/bootstrap/scope-access.js.map +1 -0
- package/dist/bootstrap/state-seams.d.ts +14 -0
- package/dist/bootstrap/state-seams.d.ts.map +1 -0
- package/dist/bootstrap/state-seams.js +26 -0
- package/dist/bootstrap/state-seams.js.map +1 -0
- package/dist/bootstrap/tool-lifecycle.d.ts +102 -0
- package/dist/bootstrap/tool-lifecycle.d.ts.map +1 -0
- package/dist/bootstrap/tool-lifecycle.js +103 -0
- package/dist/bootstrap/tool-lifecycle.js.map +1 -0
- package/dist/bootstrap/tool-trust.d.ts +49 -0
- package/dist/bootstrap/tool-trust.d.ts.map +1 -0
- package/dist/bootstrap/tool-trust.js +65 -0
- package/dist/bootstrap/tool-trust.js.map +1 -0
- package/dist/bootstrap/validate-tool.d.ts +22 -0
- package/dist/bootstrap/validate-tool.d.ts.map +1 -0
- package/dist/bootstrap/validate-tool.js +38 -0
- package/dist/bootstrap/validate-tool.js.map +1 -0
- package/dist/cli-context.d.ts +38 -0
- package/dist/cli-context.d.ts.map +1 -0
- package/dist/cli-context.js +134 -0
- package/dist/cli-context.js.map +1 -0
- package/dist/commands/agent-catalog.d.ts +45 -0
- package/dist/commands/agent-catalog.d.ts.map +1 -0
- package/dist/commands/agent-catalog.js +115 -0
- package/dist/commands/agent-catalog.js.map +1 -0
- package/dist/commands/assemble-outcome.d.ts +69 -0
- package/dist/commands/assemble-outcome.d.ts.map +1 -0
- package/dist/commands/assemble-outcome.js +121 -0
- package/dist/commands/assemble-outcome.js.map +1 -0
- package/dist/commands/clear.d.ts +32 -0
- package/dist/commands/clear.d.ts.map +1 -0
- package/dist/commands/clear.js +73 -0
- package/dist/commands/clear.js.map +1 -0
- package/dist/commands/completion.d.ts +90 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +233 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/configure.d.ts +32 -0
- package/dist/commands/configure.d.ts.map +1 -0
- package/dist/commands/configure.js +94 -0
- package/dist/commands/configure.js.map +1 -0
- package/dist/commands/history.d.ts +18 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +48 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/host-command-specs.d.ts +49 -0
- package/dist/commands/host-command-specs.d.ts.map +1 -0
- package/dist/commands/host-command-specs.js +331 -0
- package/dist/commands/host-command-specs.js.map +1 -0
- package/dist/commands/host-subcommand-groups.d.ts +69 -0
- package/dist/commands/host-subcommand-groups.d.ts.map +1 -0
- package/dist/commands/host-subcommand-groups.js +374 -0
- package/dist/commands/host-subcommand-groups.js.map +1 -0
- package/dist/commands/index.d.ts +36 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +36 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init/config-templates.d.ts +16 -0
- package/dist/commands/init/config-templates.d.ts.map +1 -0
- package/dist/commands/init/config-templates.js +108 -0
- package/dist/commands/init/config-templates.js.map +1 -0
- package/dist/commands/init/file-classifier.d.ts +40 -0
- package/dist/commands/init/file-classifier.d.ts.map +1 -0
- package/dist/commands/init/file-classifier.js +155 -0
- package/dist/commands/init/file-classifier.js.map +1 -0
- package/dist/commands/init/language-detection.d.ts +44 -0
- package/dist/commands/init/language-detection.d.ts.map +1 -0
- package/dist/commands/init/language-detection.js +124 -0
- package/dist/commands/init/language-detection.js.map +1 -0
- package/dist/commands/init/scaffold-writer.d.ts +26 -0
- package/dist/commands/init/scaffold-writer.d.ts.map +1 -0
- package/dist/commands/init/scaffold-writer.js +102 -0
- package/dist/commands/init/scaffold-writer.js.map +1 -0
- package/dist/commands/init/state-machine.d.ts +32 -0
- package/dist/commands/init/state-machine.d.ts.map +1 -0
- package/dist/commands/init/state-machine.js +105 -0
- package/dist/commands/init/state-machine.js.map +1 -0
- package/dist/commands/init.d.ts +95 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +209 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mount-command-spec.d.ts +106 -0
- package/dist/commands/mount-command-spec.d.ts.map +1 -0
- package/dist/commands/mount-command-spec.js +313 -0
- package/dist/commands/mount-command-spec.js.map +1 -0
- package/dist/commands/mount-result-command.d.ts +71 -0
- package/dist/commands/mount-result-command.d.ts.map +1 -0
- package/dist/commands/mount-result-command.js +76 -0
- package/dist/commands/mount-result-command.js.map +1 -0
- package/dist/commands/plugin/config-edit.d.ts +20 -0
- package/dist/commands/plugin/config-edit.d.ts.map +1 -0
- package/dist/commands/plugin/config-edit.js +102 -0
- package/dist/commands/plugin/config-edit.js.map +1 -0
- package/dist/commands/plugin/domain-resolution.d.ts +38 -0
- package/dist/commands/plugin/domain-resolution.d.ts.map +1 -0
- package/dist/commands/plugin/domain-resolution.js +98 -0
- package/dist/commands/plugin/domain-resolution.js.map +1 -0
- package/dist/commands/plugin/host-dir.d.ts +42 -0
- package/dist/commands/plugin/host-dir.d.ts.map +1 -0
- package/dist/commands/plugin/host-dir.js +168 -0
- package/dist/commands/plugin/host-dir.js.map +1 -0
- package/dist/commands/plugin-host-ops.d.ts +41 -0
- package/dist/commands/plugin-host-ops.d.ts.map +1 -0
- package/dist/commands/plugin-host-ops.js +114 -0
- package/dist/commands/plugin-host-ops.js.map +1 -0
- package/dist/commands/plugin.d.ts +81 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/commands/plugin.js +287 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/commands/render-outcome.d.ts +52 -0
- package/dist/commands/render-outcome.d.ts.map +1 -0
- package/dist/commands/render-outcome.js +55 -0
- package/dist/commands/render-outcome.js.map +1 -0
- package/dist/commands/session-show.d.ts +27 -0
- package/dist/commands/session-show.d.ts.map +1 -0
- package/dist/commands/session-show.js +166 -0
- package/dist/commands/session-show.js.map +1 -0
- package/dist/commands/shared.d.ts +107 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +13 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/tools/data-purge.d.ts +20 -0
- package/dist/commands/tools/data-purge.d.ts.map +1 -0
- package/dist/commands/tools/data-purge.js +59 -0
- package/dist/commands/tools/data-purge.js.map +1 -0
- package/dist/commands/tools/index.d.ts +16 -0
- package/dist/commands/tools/index.d.ts.map +1 -0
- package/dist/commands/tools/index.js +213 -0
- package/dist/commands/tools/index.js.map +1 -0
- package/dist/commands/tools/install.d.ts +24 -0
- package/dist/commands/tools/install.d.ts.map +1 -0
- package/dist/commands/tools/install.js +83 -0
- package/dist/commands/tools/install.js.map +1 -0
- package/dist/commands/tools/list.d.ts +41 -0
- package/dist/commands/tools/list.d.ts.map +1 -0
- package/dist/commands/tools/list.js +103 -0
- package/dist/commands/tools/list.js.map +1 -0
- package/dist/commands/tools/runtime-probe-entry.d.ts +14 -0
- package/dist/commands/tools/runtime-probe-entry.d.ts.map +1 -0
- package/dist/commands/tools/runtime-probe-entry.js +36 -0
- package/dist/commands/tools/runtime-probe-entry.js.map +1 -0
- package/dist/commands/tools/runtime-probe.d.ts +29 -0
- package/dist/commands/tools/runtime-probe.d.ts.map +1 -0
- package/dist/commands/tools/runtime-probe.js +66 -0
- package/dist/commands/tools/runtime-probe.js.map +1 -0
- package/dist/commands/tools/storage-contract-checks.d.ts +37 -0
- package/dist/commands/tools/storage-contract-checks.d.ts.map +1 -0
- package/dist/commands/tools/storage-contract-checks.js +91 -0
- package/dist/commands/tools/storage-contract-checks.js.map +1 -0
- package/dist/commands/tools/uninstall.d.ts +29 -0
- package/dist/commands/tools/uninstall.d.ts.map +1 -0
- package/dist/commands/tools/uninstall.js +77 -0
- package/dist/commands/tools/uninstall.js.map +1 -0
- package/dist/commands/tools/validate.d.ts +44 -0
- package/dist/commands/tools/validate.d.ts.map +1 -0
- package/dist/commands/tools/validate.js +202 -0
- package/dist/commands/tools/validate.js.map +1 -0
- package/dist/commands/uninstall/targets.d.ts +53 -0
- package/dist/commands/uninstall/targets.d.ts.map +1 -0
- package/dist/commands/uninstall/targets.js +205 -0
- package/dist/commands/uninstall/targets.js.map +1 -0
- package/dist/commands/uninstall.d.ts +88 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +184 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/env/host-env-specs.d.ts +52 -0
- package/dist/env/host-env-specs.d.ts.map +1 -0
- package/dist/env/host-env-specs.js +129 -0
- package/dist/env/host-env-specs.js.map +1 -0
- package/dist/error-handler.d.ts +64 -0
- package/dist/error-handler.d.ts.map +1 -0
- package/dist/error-handler.js +180 -0
- package/dist/error-handler.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +154 -0
- package/dist/index.js.map +1 -0
- package/dist/open-report.d.ts +40 -0
- package/dist/open-report.d.ts.map +1 -0
- package/dist/open-report.js +54 -0
- package/dist/open-report.js.map +1 -0
- package/dist/report-compose.d.ts +35 -0
- package/dist/report-compose.d.ts.map +1 -0
- package/dist/report-compose.js +103 -0
- package/dist/report-compose.js.map +1 -0
- package/dist/session-replay-registry.d.ts +20 -0
- package/dist/session-replay-registry.d.ts.map +1 -0
- package/dist/session-replay-registry.js +38 -0
- package/dist/session-replay-registry.js.map +1 -0
- package/dist/telemetry/profiling.d.ts +42 -0
- package/dist/telemetry/profiling.d.ts.map +1 -0
- package/dist/telemetry/profiling.js +160 -0
- package/dist/telemetry/profiling.js.map +1 -0
- package/dist/telemetry/sdk-init.d.ts +87 -0
- package/dist/telemetry/sdk-init.d.ts.map +1 -0
- package/dist/telemetry/sdk-init.js +235 -0
- package/dist/telemetry/sdk-init.js.map +1 -0
- package/dist/ui/App.d.ts +32 -0
- package/dist/ui/App.d.ts.map +1 -0
- package/dist/ui/App.js +35 -0
- package/dist/ui/App.js.map +1 -0
- package/dist/ui/render.d.ts +15 -0
- package/dist/ui/render.d.ts.map +1 -0
- package/dist/ui/render.js +21 -0
- package/dist/ui/render.js.map +1 -0
- package/dist/ui/result-to-view.d.ts +40 -0
- package/dist/ui/result-to-view.d.ts.map +1 -0
- package/dist/ui/result-to-view.js +389 -0
- package/dist/ui/result-to-view.js.map +1 -0
- package/dist/ui/views/init-view.d.ts +9 -0
- package/dist/ui/views/init-view.d.ts.map +1 -0
- package/dist/ui/views/init-view.js +119 -0
- package/dist/ui/views/init-view.js.map +1 -0
- package/dist/ui/views/misc-views.d.ts +18 -0
- package/dist/ui/views/misc-views.d.ts.map +1 -0
- package/dist/ui/views/misc-views.js +244 -0
- package/dist/ui/views/misc-views.js.map +1 -0
- package/dist/ui/views/plugin-view.d.ts +8 -0
- package/dist/ui/views/plugin-view.d.ts.map +1 -0
- package/dist/ui/views/plugin-view.js +135 -0
- package/dist/ui/views/plugin-view.js.map +1 -0
- package/dist/ui/views/tools-views.d.ts +12 -0
- package/dist/ui/views/tools-views.d.ts.map +1 -0
- package/dist/ui/views/tools-views.js +152 -0
- package/dist/ui/views/tools-views.js.map +1 -0
- package/dist/update-notifier.d.ts +108 -0
- package/dist/update-notifier.d.ts.map +1 -0
- package/dist/update-notifier.js +188 -0
- package/dist/update-notifier.js.map +1 -0
- package/dist/update-state.d.ts +40 -0
- package/dist/update-state.d.ts.map +1 -0
- package/dist/update-state.js +81 -0
- package/dist/update-state.js.map +1 -0
- package/dist/welcome.d.ts +53 -0
- package/dist/welcome.d.ts.map +1 -0
- package/dist/welcome.js +89 -0
- package/dist/welcome.js.map +1 -0
- package/package.json +100 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-existing file classification for `opensip init`.
|
|
3
|
+
*
|
|
4
|
+
* Walks `opensip-cli/` (skipping `.runtime/`) and tags each file as
|
|
5
|
+
* 'scaffolded', 'stale-scaffolded', or 'custom' so `--keep` / `--remove`
|
|
6
|
+
* can decide what to overwrite and what to preserve.
|
|
7
|
+
*/
|
|
8
|
+
import { createHash } from 'node:crypto';
|
|
9
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
10
|
+
import { basename as pathBasename, join } from 'node:path';
|
|
11
|
+
import { ALL_LANGUAGES } from './language-detection.js';
|
|
12
|
+
/**
|
|
13
|
+
* Build the full set of scaffold templates that init would write for the given
|
|
14
|
+
* language set — registry-driven (ADR-0038): each tool's `scaffoldExamples` over
|
|
15
|
+
* the current languages, mapped to `userPluginDir(domain, file.kind)/filename`.
|
|
16
|
+
* Same bytes as before; the classifier detects "scaffolded" files via SHA-256.
|
|
17
|
+
*/
|
|
18
|
+
function buildScaffoldTemplates(paths, languages, toolScaffolds) {
|
|
19
|
+
const templates = new Map();
|
|
20
|
+
for (const ts of toolScaffolds) {
|
|
21
|
+
if (!ts.scaffoldExamples)
|
|
22
|
+
continue;
|
|
23
|
+
for (const file of ts.scaffoldExamples({ languages })) {
|
|
24
|
+
templates.set(join(paths.userPluginDir(ts.layout.domain, file.kind), file.filename), file.content);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return templates;
|
|
28
|
+
}
|
|
29
|
+
function sha256(content) {
|
|
30
|
+
return createHash('sha256').update(content).digest('hex');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Walk every file under `opensip-cli/` (excluding `.runtime/`, which
|
|
34
|
+
* is gitignored runtime state and not user-authored) and tag each one.
|
|
35
|
+
*
|
|
36
|
+
* Classification rules:
|
|
37
|
+
*
|
|
38
|
+
* - 'scaffolded' — content matches a current-template byte-for-byte.
|
|
39
|
+
* - 'stale-scaffolded' — file shape matches a previous-language scaffold:
|
|
40
|
+
* filename `example-check-<lang>.mjs` for <lang>
|
|
41
|
+
* NOT in the current detection set, OR the file
|
|
42
|
+
* carries a pinned EXAMPLE_CHECK_IDS UUID for a
|
|
43
|
+
* language not in the current set.
|
|
44
|
+
* - 'custom' — anything else (user-authored).
|
|
45
|
+
*
|
|
46
|
+
* The walk is bounded to the `opensip-cli/` subtree (kilobytes in
|
|
47
|
+
* practice). The `.runtime/` subdir is skipped so caches/logs/sessions
|
|
48
|
+
* don't pollute the file list.
|
|
49
|
+
*
|
|
50
|
+
* Note on hash-based scaffolded detection: a scaffolded file that has
|
|
51
|
+
* been line-ending normalized (CRLF↔LF) or stripped of a trailing
|
|
52
|
+
* newline by an editor will not byte-for-byte match the template. In
|
|
53
|
+
* that case it falls back to 'custom' or 'stale-scaffolded' (when the
|
|
54
|
+
* UUID still matches but content drifted). The UUID-based fallback
|
|
55
|
+
* catches the common "stale language" case; cosmetic drift on a
|
|
56
|
+
* current-language file is treated as custom — which is the safer
|
|
57
|
+
* outcome (won't silently overwrite).
|
|
58
|
+
*/
|
|
59
|
+
export function classifyFiles(paths, currentLanguages, toolScaffolds) {
|
|
60
|
+
if (!existsSync(paths.userSourceDir))
|
|
61
|
+
return [];
|
|
62
|
+
const templates = buildScaffoldTemplates(paths, currentLanguages, toolScaffolds);
|
|
63
|
+
const templateHashes = new Map();
|
|
64
|
+
for (const [absPath, body] of templates) {
|
|
65
|
+
templateHashes.set(absPath, sha256(body));
|
|
66
|
+
}
|
|
67
|
+
const currentLangSet = new Set(currentLanguages);
|
|
68
|
+
// Stale-id detection (ADR-0038): the COMPLETE id universe each tool owns
|
|
69
|
+
// (`stableExampleIds`), minus the ids the CURRENT languages scaffold. A file
|
|
70
|
+
// carrying a complete-but-not-current id is a stale scaffold for a config the
|
|
71
|
+
// project no longer uses; current-config ids are excluded so an edited
|
|
72
|
+
// current-language example (no content-hash match) stays `custom`.
|
|
73
|
+
const completeIds = new Set(toolScaffolds.flatMap((ts) => ts.stableExampleIds?.() ?? []));
|
|
74
|
+
const currentIds = new Set(toolScaffolds.flatMap((ts) => (ts.scaffoldExamples?.({ languages: currentLanguages }) ?? []).map((f) => f.stableId)));
|
|
75
|
+
const staleIds = [...completeIds].filter((id) => !currentIds.has(id));
|
|
76
|
+
const out = [];
|
|
77
|
+
// Walk the dir, skipping `.runtime/`.
|
|
78
|
+
const visit = (dir) => {
|
|
79
|
+
let entries;
|
|
80
|
+
try {
|
|
81
|
+
entries = readdirSync(dir);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// @fitness-ignore-next-line error-handling-quality -- directory walk probe: an unreadable directory means "no entries to classify here", same as a genuinely empty dir; failure IS the signal.
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
for (const name of entries) {
|
|
88
|
+
const full = join(dir, name);
|
|
89
|
+
// Skip runtime state (gitignored, tool-managed).
|
|
90
|
+
if (full === paths.runtimeDir)
|
|
91
|
+
continue;
|
|
92
|
+
let st;
|
|
93
|
+
try {
|
|
94
|
+
st = statSync(full);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (st.isDirectory()) {
|
|
100
|
+
visit(full);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (!st.isFile())
|
|
104
|
+
continue;
|
|
105
|
+
out.push(classifyOneFile(full, templateHashes, currentLangSet, staleIds));
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
visit(paths.userSourceDir);
|
|
109
|
+
// Stable order — relative path ascending — so callers can render a
|
|
110
|
+
// deterministic list and tests can assert without sort gymnastics.
|
|
111
|
+
out.sort((a, b) => a.path.localeCompare(b.path));
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
const STALE_FILENAME_PATTERN = /^example-check-([a-z+]+)\.mjs$/;
|
|
115
|
+
function classifyOneFile(absPath, templateHashes, currentLangSet, staleIds) {
|
|
116
|
+
let content;
|
|
117
|
+
try {
|
|
118
|
+
content = readFileSync(absPath, 'utf8');
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Unreadable: surface as custom so we err on the side of preservation.
|
|
122
|
+
return { path: absPath, classification: 'custom' };
|
|
123
|
+
}
|
|
124
|
+
// 1) Content-hash match against current-template set.
|
|
125
|
+
const hash = sha256(content);
|
|
126
|
+
if (templateHashes.get(absPath) === hash) {
|
|
127
|
+
return { path: absPath, classification: 'scaffolded' };
|
|
128
|
+
}
|
|
129
|
+
// 2) Stale-by-filename: example-check-<lang>.mjs for <lang> not in
|
|
130
|
+
// current set.
|
|
131
|
+
// Use node:path basename so the separator extraction works on Windows.
|
|
132
|
+
// A hard-coded '/' would leave `basename` equal to the whole absolute
|
|
133
|
+
// Windows path (`lastIndexOf('/')` returns -1), and STALE_FILENAME_PATTERN
|
|
134
|
+
// would never match — silently misclassifying every stale-scaffolded
|
|
135
|
+
// file as 'custom' and breaking `--keep` semantics on Windows.
|
|
136
|
+
const basename = pathBasename(absPath);
|
|
137
|
+
const filenameMatch = STALE_FILENAME_PATTERN.exec(basename);
|
|
138
|
+
if (filenameMatch) {
|
|
139
|
+
const fileLang = filenameMatch[1];
|
|
140
|
+
if (fileLang &&
|
|
141
|
+
!currentLangSet.has(fileLang) &&
|
|
142
|
+
ALL_LANGUAGES.includes(fileLang)) {
|
|
143
|
+
return { path: absPath, classification: 'stale-scaffolded' };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// 3) Stale-by-pinned-id: any of the aggregated stale ids (the complete tool id
|
|
147
|
+
// universe minus the current-config ids) embedded in the file.
|
|
148
|
+
for (const id of staleIds) {
|
|
149
|
+
if (content.includes(id)) {
|
|
150
|
+
return { path: absPath, classification: 'stale-scaffolded' };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { path: absPath, classification: 'custom' };
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=file-classifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-classifier.js","sourceRoot":"","sources":["../../../src/commands/init/file-classifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language detection for `opensip init`.
|
|
3
|
+
*
|
|
4
|
+
* Inspects the cwd for well-known language markers (Cargo.toml,
|
|
5
|
+
* pyproject.toml, go.mod, pom.xml/build.gradle, CMakeLists.txt,
|
|
6
|
+
* tsconfig.json/package.json) and returns the unique set of detected
|
|
7
|
+
* languages. Also parses the `--language <comma-separated>` flag that
|
|
8
|
+
* overrides detection.
|
|
9
|
+
*/
|
|
10
|
+
export type SupportedLanguage = 'typescript' | 'rust' | 'python' | 'go' | 'java' | 'cpp';
|
|
11
|
+
export declare const ALL_LANGUAGES: readonly SupportedLanguage[];
|
|
12
|
+
/**
|
|
13
|
+
* Inspect the cwd for language markers. Returns the unique set of
|
|
14
|
+
* detected languages. TypeScript is detected only when there's a
|
|
15
|
+
* `tsconfig.json` (or a `package.json` with no other marker
|
|
16
|
+
* present — fallback for plain JS/TS projects).
|
|
17
|
+
*/
|
|
18
|
+
export declare function detectLanguages(cwd: string): SupportedLanguage[];
|
|
19
|
+
/**
|
|
20
|
+
* Parse the `--language <comma-separated>` argv string into a list of
|
|
21
|
+
* known languages. Throws on unknown entries — the CLI surfaces it as
|
|
22
|
+
* an exit-2 configuration error.
|
|
23
|
+
*
|
|
24
|
+
* @throws {Error} When `raw` contains a token not present in `ALL_LANGUAGES`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseLanguageFlag(raw: string): SupportedLanguage[];
|
|
27
|
+
export type LanguageResolution = {
|
|
28
|
+
ok: true;
|
|
29
|
+
languages: SupportedLanguage[];
|
|
30
|
+
} | {
|
|
31
|
+
ok: false;
|
|
32
|
+
error: {
|
|
33
|
+
detected: SupportedLanguage[];
|
|
34
|
+
message: string;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Resolve the language set for an init run: either parse the explicit
|
|
39
|
+
* --language flag, or fall back to filesystem detection. Returns an
|
|
40
|
+
* error variant when no markers are found, or when multiple languages
|
|
41
|
+
* are detected without --language to disambiguate.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveLanguages(cwd: string, languageFlag: readonly string[] | undefined): LanguageResolution;
|
|
44
|
+
//# sourceMappingURL=language-detection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-detection.d.ts","sourceRoot":"","sources":["../../../src/commands/init/language-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,KAAK,CAAC;AAEzF,eAAO,MAAM,aAAa,EAAE,SAAS,iBAAiB,EAOrD,CAAC;AAmBF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAiBhE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAmBlE;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,iBAAiB,EAAE,CAAA;CAAE,GAC5C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAE7E;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAC1C,kBAAkB,CAuCpB"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language detection for `opensip init`.
|
|
3
|
+
*
|
|
4
|
+
* Inspects the cwd for well-known language markers (Cargo.toml,
|
|
5
|
+
* pyproject.toml, go.mod, pom.xml/build.gradle, CMakeLists.txt,
|
|
6
|
+
* tsconfig.json/package.json) and returns the unique set of detected
|
|
7
|
+
* languages. Also parses the `--language <comma-separated>` flag that
|
|
8
|
+
* overrides detection.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
export const ALL_LANGUAGES = [
|
|
13
|
+
'typescript',
|
|
14
|
+
'rust',
|
|
15
|
+
'python',
|
|
16
|
+
'go',
|
|
17
|
+
'java',
|
|
18
|
+
'cpp',
|
|
19
|
+
];
|
|
20
|
+
const ALL_LANGUAGES_SET = new Set(ALL_LANGUAGES);
|
|
21
|
+
const MARKERS = [
|
|
22
|
+
{ language: 'rust', file: 'Cargo.toml', description: 'Rust workspace' },
|
|
23
|
+
{ language: 'python', file: 'pyproject.toml', description: 'Python project' },
|
|
24
|
+
{ language: 'python', file: 'setup.py', description: 'Python project (setup.py)' },
|
|
25
|
+
{ language: 'go', file: 'go.mod', description: 'Go module' },
|
|
26
|
+
{ language: 'java', file: 'pom.xml', description: 'Maven project' },
|
|
27
|
+
{ language: 'java', file: 'build.gradle', description: 'Gradle project' },
|
|
28
|
+
{ language: 'cpp', file: 'CMakeLists.txt', description: 'CMake project' },
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Inspect the cwd for language markers. Returns the unique set of
|
|
32
|
+
* detected languages. TypeScript is detected only when there's a
|
|
33
|
+
* `tsconfig.json` (or a `package.json` with no other marker
|
|
34
|
+
* present — fallback for plain JS/TS projects).
|
|
35
|
+
*/
|
|
36
|
+
export function detectLanguages(cwd) {
|
|
37
|
+
const detected = new Set();
|
|
38
|
+
for (const marker of MARKERS) {
|
|
39
|
+
if (existsSync(join(cwd, marker.file)))
|
|
40
|
+
detected.add(marker.language);
|
|
41
|
+
}
|
|
42
|
+
// TypeScript: tsconfig.json is the strong signal. package.json alone
|
|
43
|
+
// is ambiguous — a Rust project might have a docs site with one — but
|
|
44
|
+
// ONLY when no other marker is present, treat it as a TS project.
|
|
45
|
+
const hasTsconfig = existsSync(join(cwd, 'tsconfig.json'));
|
|
46
|
+
const hasPackageJson = existsSync(join(cwd, 'package.json'));
|
|
47
|
+
if (hasTsconfig || (hasPackageJson && detected.size === 0)) {
|
|
48
|
+
detected.add('typescript');
|
|
49
|
+
}
|
|
50
|
+
return [...detected];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parse the `--language <comma-separated>` argv string into a list of
|
|
54
|
+
* known languages. Throws on unknown entries — the CLI surfaces it as
|
|
55
|
+
* an exit-2 configuration error.
|
|
56
|
+
*
|
|
57
|
+
* @throws {Error} When `raw` contains a token not present in `ALL_LANGUAGES`.
|
|
58
|
+
*/
|
|
59
|
+
export function parseLanguageFlag(raw) {
|
|
60
|
+
const out = [];
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
for (const part of raw.split(',')) {
|
|
63
|
+
const trimmed = part.trim().toLowerCase();
|
|
64
|
+
if (trimmed.length === 0)
|
|
65
|
+
continue;
|
|
66
|
+
if (seen.has(trimmed))
|
|
67
|
+
continue;
|
|
68
|
+
if (!ALL_LANGUAGES_SET.has(trimmed)) {
|
|
69
|
+
throw new Error(`Unknown language '${trimmed}'. Expected one of: ${ALL_LANGUAGES.join(', ')}`);
|
|
70
|
+
}
|
|
71
|
+
seen.add(trimmed);
|
|
72
|
+
out.push(trimmed);
|
|
73
|
+
}
|
|
74
|
+
if (out.length === 0) {
|
|
75
|
+
throw new Error('--language received an empty list');
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the language set for an init run: either parse the explicit
|
|
81
|
+
* --language flag, or fall back to filesystem detection. Returns an
|
|
82
|
+
* error variant when no markers are found, or when multiple languages
|
|
83
|
+
* are detected without --language to disambiguate.
|
|
84
|
+
*/
|
|
85
|
+
export function resolveLanguages(cwd, languageFlag) {
|
|
86
|
+
if (languageFlag && languageFlag.length > 0) {
|
|
87
|
+
try {
|
|
88
|
+
// The flag is an accumulator (`--language ts --language rust`) whose
|
|
89
|
+
// elements may also be comma-separated (`--language ts,rust`). Join into
|
|
90
|
+
// one canonical comma-list and delegate to the established parser, which
|
|
91
|
+
// splits, trims, dedupes, and validates.
|
|
92
|
+
return { ok: true, languages: parseLanguageFlag(languageFlag.join(',')) };
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
96
|
+
return { ok: false, error: { detected: [], message: msg } };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const detected = detectLanguages(cwd);
|
|
100
|
+
if (detected.length === 0) {
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
error: {
|
|
104
|
+
detected: [],
|
|
105
|
+
message: 'No language markers found. Specify with: ' +
|
|
106
|
+
'opensip init --language <typescript|rust|python|go|java|cpp> ' +
|
|
107
|
+
'(comma-separated for polyglot projects).',
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (detected.length > 1) {
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
error: {
|
|
115
|
+
detected,
|
|
116
|
+
message: `Detected multiple languages: ${detected.join(', ')}. ` +
|
|
117
|
+
`Pass --language <comma-separated-list> to choose. ` +
|
|
118
|
+
`Example: opensip init --language ${detected.join(',')}`,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return { ok: true, languages: detected };
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=language-detection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-detection.js","sourceRoot":"","sources":["../../../src/commands/init/language-detection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Disk-write phase for `opensip init`.
|
|
3
|
+
*
|
|
4
|
+
* Writes the config + example files, optionally preserving pre-existing
|
|
5
|
+
* custom/stale-scaffolded files (`--keep`) or wiping the user-source
|
|
6
|
+
* dir first (`--remove`). Also patches `.gitignore` to exclude the
|
|
7
|
+
* runtime state subtree.
|
|
8
|
+
*/
|
|
9
|
+
import type { SupportedLanguage } from './language-detection.js';
|
|
10
|
+
import type { WorkingDirState } from './state-machine.js';
|
|
11
|
+
import type { ToolScaffold } from '../shared.js';
|
|
12
|
+
import type { InitResult, PreExistingFile } from '@opensip-cli/contracts';
|
|
13
|
+
import type { ProjectPaths } from '@opensip-cli/core';
|
|
14
|
+
export interface ScaffoldRunInputs {
|
|
15
|
+
readonly paths: ProjectPaths;
|
|
16
|
+
readonly languages: SupportedLanguage[];
|
|
17
|
+
readonly cwd: string;
|
|
18
|
+
readonly state: WorkingDirState;
|
|
19
|
+
readonly preExistingFiles: readonly PreExistingFile[];
|
|
20
|
+
readonly removeFirst: boolean;
|
|
21
|
+
readonly keepCustom: boolean;
|
|
22
|
+
/** Per-tool scaffold contributions (ADR-0038) — the directory layout + example bytes. */
|
|
23
|
+
readonly toolScaffolds: readonly ToolScaffold[];
|
|
24
|
+
}
|
|
25
|
+
export declare function runScaffold(inputs: ScaffoldRunInputs, baseResult: Pick<InitResult, 'type' | 'path' | 'cwd' | 'configFilename'>): InitResult;
|
|
26
|
+
//# sourceMappingURL=scaffold-writer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold-writer.d.ts","sourceRoot":"","sources":["../../../src/commands/init/scaffold-writer.ts"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,mBAAmB,CAAC;AA2FvE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,iBAAiB,EAAE,CAAC;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,QAAQ,CAAC,gBAAgB,EAAE,SAAS,eAAe,EAAE,CAAC;IACtD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,aAAa,EAAE,SAAS,YAAY,EAAE,CAAC;CACjD;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,iBAAiB,EACzB,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,gBAAgB,CAAC,GACvE,UAAU,CAuCZ"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// @fitness-ignore-file unbounded-memory -- reads scaffold template files shipped with the CLI; bounded by the package's own embedded examples
|
|
2
|
+
/**
|
|
3
|
+
* Disk-write phase for `opensip init`.
|
|
4
|
+
*
|
|
5
|
+
* Writes the config + example files, optionally preserving pre-existing
|
|
6
|
+
* custom/stale-scaffolded files (`--keep`) or wiping the user-source
|
|
7
|
+
* dir first (`--remove`). Also patches `.gitignore` to exclude the
|
|
8
|
+
* runtime state subtree.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { generateConfig } from './config-templates.js';
|
|
13
|
+
const GITIGNORE_LINE = 'opensip-cli/.runtime/';
|
|
14
|
+
function ensureGitignore(cwd) {
|
|
15
|
+
const path = join(cwd, '.gitignore');
|
|
16
|
+
if (!existsSync(path)) {
|
|
17
|
+
writeFileSync(path, `${GITIGNORE_LINE}\n`, 'utf8');
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
const content = readFileSync(path, 'utf8');
|
|
21
|
+
// @fitness-ignore-next-line silent-early-returns -- idempotent "did I modify?" return: boolean IS the function's contract (caller dispatches on "already present" vs "I appended")
|
|
22
|
+
if (content.split('\n').some((line) => line.trim() === GITIGNORE_LINE)) {
|
|
23
|
+
return false; // already present, idempotent
|
|
24
|
+
}
|
|
25
|
+
const sep = content.endsWith('\n') ? '' : '\n';
|
|
26
|
+
writeFileSync(path, `${content}${sep}\n# opensip-cli runtime state\n${GITIGNORE_LINE}\n`, 'utf8');
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Write `content` to `filePath` according to the scaffold rules:
|
|
31
|
+
* - keepCustom = false → always overwrite (or create) the file.
|
|
32
|
+
* - keepCustom = true → preserve any pre-existing file we classified
|
|
33
|
+
* as 'custom' or 'stale-scaffolded'; overwrite
|
|
34
|
+
* 'scaffolded' files (and create missing ones).
|
|
35
|
+
*/
|
|
36
|
+
function writeScaffoldedFile(filePath, content, keepCustom, preExistingByPath, createdFiles) {
|
|
37
|
+
if (keepCustom) {
|
|
38
|
+
const existing = preExistingByPath.get(filePath);
|
|
39
|
+
// Preserve user content. Stale-scaffolded is also preserved — the
|
|
40
|
+
// user may have been working with it; we surface it but don't
|
|
41
|
+
// overwrite.
|
|
42
|
+
if (existing &&
|
|
43
|
+
(existing.classification === 'custom' || existing.classification === 'stale-scaffolded')) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
writeFileSync(filePath, content, 'utf8');
|
|
48
|
+
createdFiles.push(filePath);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* ADR-0038: generic, registry-driven scaffold. For each registered tool's
|
|
52
|
+
* contribution: `mkdir` every `userSubdirs` kind under its `pluginLayout.domain`,
|
|
53
|
+
* then write each `ScaffoldFile` the tool's `scaffoldExamples(ctx)` returns under
|
|
54
|
+
* `userPluginDir(domain, file.kind)/file.filename`. No fit/sim/checks/recipes
|
|
55
|
+
* literals — the directory layout comes from each tool's `pluginLayout`, the
|
|
56
|
+
* example bytes from the tool (ADR-0009 corollary 1).
|
|
57
|
+
*/
|
|
58
|
+
function scaffoldToolExamples(paths, toolScaffolds, ctx, options, createdFiles) {
|
|
59
|
+
for (const ts of toolScaffolds) {
|
|
60
|
+
if (!ts.scaffoldExamples)
|
|
61
|
+
continue;
|
|
62
|
+
for (const kind of ts.layout.userSubdirs) {
|
|
63
|
+
mkdirSync(paths.userPluginDir(ts.layout.domain, kind), { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
for (const file of ts.scaffoldExamples(ctx)) {
|
|
66
|
+
writeScaffoldedFile(join(paths.userPluginDir(ts.layout.domain, file.kind), file.filename), file.content, options.keepCustom, options.preExistingByPath, createdFiles);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function runScaffold(inputs, baseResult) {
|
|
71
|
+
const { paths, languages, cwd, state, preExistingFiles, removeFirst, keepCustom, toolScaffolds } = inputs;
|
|
72
|
+
// --remove: blow away the user-source dir before scaffolding. The
|
|
73
|
+
// config file is always rewritten below regardless.
|
|
74
|
+
if (removeFirst && existsSync(paths.userSourceDir)) {
|
|
75
|
+
rmSync(paths.userSourceDir, { recursive: true, force: true });
|
|
76
|
+
}
|
|
77
|
+
const createdFiles = [];
|
|
78
|
+
// Always rewrite the config — its content is a function of the
|
|
79
|
+
// selected languages, and a re-init with new --language values must
|
|
80
|
+
// refresh it. (The legacy code rewrote it unconditionally on --force
|
|
81
|
+
// too; we keep that semantics.)
|
|
82
|
+
writeFileSync(paths.configFile, generateConfig(languages, toolScaffolds), 'utf8');
|
|
83
|
+
createdFiles.push(paths.configFile);
|
|
84
|
+
// After --remove the dir is gone, so nothing pre-existed; pass an
|
|
85
|
+
// empty map so writeScaffoldedFile creates everything fresh.
|
|
86
|
+
const preExistingByPath = removeFirst
|
|
87
|
+
? new Map()
|
|
88
|
+
: new Map(preExistingFiles.map((f) => [f.path, f]));
|
|
89
|
+
const ctx = { languages };
|
|
90
|
+
scaffoldToolExamples(paths, toolScaffolds, ctx, { keepCustom, preExistingByPath }, createdFiles);
|
|
91
|
+
const gitignoreUpdated = ensureGitignore(cwd);
|
|
92
|
+
return {
|
|
93
|
+
...baseResult,
|
|
94
|
+
created: true,
|
|
95
|
+
state,
|
|
96
|
+
languages,
|
|
97
|
+
createdFiles,
|
|
98
|
+
gitignoreUpdated,
|
|
99
|
+
preExistingFiles: state === 'pristine' ? [] : preExistingFiles,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=scaffold-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold-writer.js","sourceRoot":"","sources":["../../../src/commands/init/scaffold-writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Working-directory state classification for `opensip init`.
|
|
3
|
+
*
|
|
4
|
+
* Decides one of four states (pristine / fully-initialized /
|
|
5
|
+
* partial-config-only / partial-dir-only) based on whether the config
|
|
6
|
+
* file and the user-source dir exist, and renders the partial-state
|
|
7
|
+
* refusal message + the "discovered inside existing project" message.
|
|
8
|
+
*/
|
|
9
|
+
import type { InitResult, PreExistingFile } from '@opensip-cli/contracts';
|
|
10
|
+
import type { ProjectPaths } from '@opensip-cli/core';
|
|
11
|
+
export type WorkingDirState = NonNullable<InitResult['state']>;
|
|
12
|
+
/**
|
|
13
|
+
* Classify the working directory into one of four states based on the
|
|
14
|
+
* presence of the config file and the `opensip-cli/` directory.
|
|
15
|
+
*
|
|
16
|
+
* The dir-presence check ignores `opensip-cli/.runtime/` — that
|
|
17
|
+
* subtree is tool-managed (logs, sessions, caches, plugin installs)
|
|
18
|
+
* and the CLI's `preAction` hook creates `.runtime/logs/` before any
|
|
19
|
+
* subcommand runs. Treating a runtime-only dir as a "partial-dir"
|
|
20
|
+
* would misclassify a pristine project the moment the bootstrap hook
|
|
21
|
+
* touches the disk. User-authored content lives under
|
|
22
|
+
* `opensip-cli/{fit,sim}/`; only those count as "the dir is present".
|
|
23
|
+
*/
|
|
24
|
+
export declare function classifyWorkingDir(paths: ProjectPaths): WorkingDirState;
|
|
25
|
+
export declare function buildPartialStateMessage(state: WorkingDirState, preExistingFiles: readonly PreExistingFile[], cwd: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Build the "✗ This directory is already inside an OpenSIP CLI project"
|
|
28
|
+
* refusal message. Same string is embedded in the InitResult.insideExistingProject
|
|
29
|
+
* for --json consumers and rendered by InitFeedback for human-readable output.
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatInsideExistingProjectMessage(discoveredRoot: string): string;
|
|
32
|
+
//# sourceMappingURL=state-machine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine.d.ts","sourceRoot":"","sources":["../../../src/commands/init/state-machine.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;AAE/D;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,eAAe,CAOvE;AAoBD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,eAAe,EACtB,gBAAgB,EAAE,SAAS,eAAe,EAAE,EAC5C,GAAG,EAAE,MAAM,GACV,MAAM,CAmCR;AAED;;;;GAIG;AACH,wBAAgB,kCAAkC,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAkBjF"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Working-directory state classification for `opensip init`.
|
|
3
|
+
*
|
|
4
|
+
* Decides one of four states (pristine / fully-initialized /
|
|
5
|
+
* partial-config-only / partial-dir-only) based on whether the config
|
|
6
|
+
* file and the user-source dir exist, and renders the partial-state
|
|
7
|
+
* refusal message + the "discovered inside existing project" message.
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
10
|
+
import { relative } from 'node:path';
|
|
11
|
+
/**
|
|
12
|
+
* Classify the working directory into one of four states based on the
|
|
13
|
+
* presence of the config file and the `opensip-cli/` directory.
|
|
14
|
+
*
|
|
15
|
+
* The dir-presence check ignores `opensip-cli/.runtime/` — that
|
|
16
|
+
* subtree is tool-managed (logs, sessions, caches, plugin installs)
|
|
17
|
+
* and the CLI's `preAction` hook creates `.runtime/logs/` before any
|
|
18
|
+
* subcommand runs. Treating a runtime-only dir as a "partial-dir"
|
|
19
|
+
* would misclassify a pristine project the moment the bootstrap hook
|
|
20
|
+
* touches the disk. User-authored content lives under
|
|
21
|
+
* `opensip-cli/{fit,sim}/`; only those count as "the dir is present".
|
|
22
|
+
*/
|
|
23
|
+
export function classifyWorkingDir(paths) {
|
|
24
|
+
const hasConfig = existsSync(paths.configFile);
|
|
25
|
+
const hasDir = userSourceDirHasUserContent(paths);
|
|
26
|
+
if (!hasConfig && !hasDir)
|
|
27
|
+
return 'pristine';
|
|
28
|
+
if (hasConfig && hasDir)
|
|
29
|
+
return 'fully-initialized';
|
|
30
|
+
if (hasConfig)
|
|
31
|
+
return 'partial-config-only';
|
|
32
|
+
return 'partial-dir-only';
|
|
33
|
+
}
|
|
34
|
+
function userSourceDirHasUserContent(paths) {
|
|
35
|
+
if (!existsSync(paths.userSourceDir))
|
|
36
|
+
return false;
|
|
37
|
+
let entries;
|
|
38
|
+
try {
|
|
39
|
+
entries = readdirSync(paths.userSourceDir);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// @fitness-ignore-next-line error-handling-quality -- directory probe: an unreadable userSourceDir means "no user content present", treated identically to an empty dir; failure IS the signal.
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
// Anything other than `.runtime/` (tool-managed) counts as user content.
|
|
46
|
+
return entries.some((name) => name !== '.runtime');
|
|
47
|
+
}
|
|
48
|
+
function relativize(absPath, cwd) {
|
|
49
|
+
const rel = relative(cwd, absPath);
|
|
50
|
+
return rel === '' ? absPath : rel;
|
|
51
|
+
}
|
|
52
|
+
export function buildPartialStateMessage(state, preExistingFiles, cwd) {
|
|
53
|
+
const lines = [];
|
|
54
|
+
switch (state) {
|
|
55
|
+
case 'fully-initialized': {
|
|
56
|
+
lines.push('OpenSIP CLI is already initialized in this directory.');
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case 'partial-config-only': {
|
|
60
|
+
lines.push('opensip-cli.config.yml exists but opensip-cli/ does not.');
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
case 'partial-dir-only': {
|
|
64
|
+
lines.push('opensip-cli/ exists but opensip-cli.config.yml does not.');
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
case 'pristine': {
|
|
68
|
+
// Should not happen in this code path — pristine never errors.
|
|
69
|
+
return 'Unexpected pristine state.';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (preExistingFiles.length > 0) {
|
|
73
|
+
lines.push('', `Found ${String(preExistingFiles.length)} file(s) under opensip-cli/:`);
|
|
74
|
+
for (const f of preExistingFiles) {
|
|
75
|
+
lines.push(` ${relativize(f.path, cwd)} (${f.classification})`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
lines.push('', 'Choose one:', ' opensip init --keep Re-scaffold examples; preserve custom files.', ' opensip init --remove Delete opensip-cli/ and scaffold fresh.');
|
|
79
|
+
return lines.join('\n');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Build the "✗ This directory is already inside an OpenSIP CLI project"
|
|
83
|
+
* refusal message. Same string is embedded in the InitResult.insideExistingProject
|
|
84
|
+
* for --json consumers and rendered by InitFeedback for human-readable output.
|
|
85
|
+
*/
|
|
86
|
+
export function formatInsideExistingProjectMessage(discoveredRoot) {
|
|
87
|
+
return [
|
|
88
|
+
`✗ This directory is already inside an OpenSIP CLI project:`,
|
|
89
|
+
` ${discoveredRoot}`,
|
|
90
|
+
` (config: opensip-cli.config.yml)`,
|
|
91
|
+
``,
|
|
92
|
+
` What did you want to do?`,
|
|
93
|
+
``,
|
|
94
|
+
` • Re-scaffold examples, keep your custom files:`,
|
|
95
|
+
` opensip init --keep --cwd ${discoveredRoot}`,
|
|
96
|
+
``,
|
|
97
|
+
` • Reset the existing project (delete everything, start over):`,
|
|
98
|
+
` opensip init --remove --cwd ${discoveredRoot}`,
|
|
99
|
+
``,
|
|
100
|
+
` • Create a NEW separate project here (rare — only for`,
|
|
101
|
+
` truly independent sub-projects in a monorepo):`,
|
|
102
|
+
` opensip init --cwd .`,
|
|
103
|
+
].join('\n');
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=state-machine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine.js","sourceRoot":"","sources":["../../../src/commands/init/state-machine.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAOrC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACpD,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,2BAA2B,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM;QAAE,OAAO,UAAU,CAAC;IAC7C,IAAI,SAAS,IAAI,MAAM;QAAE,OAAO,mBAAmB,CAAC;IACpD,IAAI,SAAS;QAAE,OAAO,qBAAqB,CAAC;IAC5C,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED,SAAS,2BAA2B,CAAC,KAAmB;IACtD,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,aAAa,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,gMAAgM;QAChM,OAAO,KAAK,CAAC;IACf,CAAC;IACD,yEAAyE;IACzE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,UAAU,CAAC,OAAe,EAAE,GAAW;IAC9C,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,KAAsB,EACtB,gBAA4C,EAC5C,GAAW;IAEX,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,mBAAmB,CAAC,CAAC,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;YACpE,MAAM;QACR,CAAC;QACD,KAAK,qBAAqB,CAAC,CAAC,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC;YACvE,MAAM;QACR,CAAC;QACD,KAAK,kBAAkB,CAAC,CAAC,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC;YACvE,MAAM;QACR,CAAC;QACD,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,+DAA+D;YAC/D,OAAO,4BAA4B,CAAC;QACtC,CAAC;IACH,CAAC;IAED,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,8BAA8B,CAAC,CAAC;QACvF,KAAK,MAAM,CAAC,IAAI,gBAAgB,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CACR,EAAE,EACF,aAAa,EACb,uEAAuE,EACvE,kEAAkE,CACnE,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kCAAkC,CAAC,cAAsB;IACvE,OAAO;QACL,4DAA4D;QAC5D,OAAO,cAAc,EAAE;QACvB,sCAAsC;QACtC,EAAE;QACF,4BAA4B;QAC5B,EAAE;QACF,qDAAqD;QACrD,qCAAqC,cAAc,EAAE;QACrD,EAAE;QACF,mEAAmE;QACnE,uCAAuC,cAAc,EAAE;QACvD,EAAE;QACF,2DAA2D;QAC3D,sDAAsD;QACtD,8BAA8B;KAC/B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|