@alint-js/cli 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/bin/index.mjs +1 -1
- package/dist/{cli-Dey9TSc8.mjs → cli-CmLooFry.mjs} +126 -29
- package/dist/index.mjs +1 -1
- package/package.json +4 -8
package/README.md
CHANGED
|
@@ -274,6 +274,41 @@ Use `alint plugin install` after adding or changing plugin sources.
|
|
|
274
274
|
- **Package:** `@scope/alint-plugin-example1@1.2.3` downloads that exact version into `.alint/plugins/store`, verifies its integrity, and loads its root export.
|
|
275
275
|
- **Local:** `./plugins/my-plugin` or an absolute directory loads the current package root export in place. `alint` checks that the package and entry exist and that the entry stays inside the package directory. It does not build the plugin or install the plugin's dependencies.
|
|
276
276
|
|
|
277
|
+
#### Declarative local rule plugins
|
|
278
|
+
|
|
279
|
+
For simple prompt-backed rules, a local plugin directory can omit `package.json` and contain one or more `rule.alint.*` files:
|
|
280
|
+
|
|
281
|
+
```toml
|
|
282
|
+
# rules/architecture/semantic/rule.alint.toml
|
|
283
|
+
name = "semantic-boundary"
|
|
284
|
+
builtInAgent = "basic-structured"
|
|
285
|
+
instruction = """
|
|
286
|
+
Find semantic boundary problems in the reviewed source.
|
|
287
|
+
Report concrete findings with line numbers and short suggestions.
|
|
288
|
+
"""
|
|
289
|
+
includeFiles = [ "src/**/*.py" ]
|
|
290
|
+
excludeFiles = [
|
|
291
|
+
"**/*_test.py",
|
|
292
|
+
"**/vendor/**",
|
|
293
|
+
]
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Reference the directory through the same static `plugins` table:
|
|
297
|
+
|
|
298
|
+
```toml
|
|
299
|
+
[[config.group]]
|
|
300
|
+
files = [ "src/**/*.py" ]
|
|
301
|
+
language = "text/plain"
|
|
302
|
+
|
|
303
|
+
[config.group.plugins]
|
|
304
|
+
arch = "./rules/architecture"
|
|
305
|
+
|
|
306
|
+
[config.group.rules]
|
|
307
|
+
"arch/semantic-boundary" = "warn"
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`name` becomes the local rule id. `builtInAgent` can be `basic-structured` for a prompt-only structured-output rule or `basic-coding-agent` for a small built-in agent with filesystem tools. `includeFiles` and `excludeFiles` define where diagnostics may be reported; they do not limit what `basic-coding-agent` can inspect.
|
|
311
|
+
|
|
277
312
|
Run the install command again after changing a source string, moving a local directory, or changing its symlink target. Changes inside the same local directory are loaded by the next CLI process without reinstalling.
|
|
278
313
|
|
|
279
314
|
Local plugins execute as trusted Node.js code. Directory containment checks validate the installed source; they are not a sandbox.
|
package/dist/bin/index.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import Gitignore from "gitignore-fs";
|
|
|
14
14
|
import { Minimatch, minimatch } from "minimatch";
|
|
15
15
|
import { errorMessageFrom as errorMessageFrom$1 } from "@moeru/std";
|
|
16
16
|
//#region package.json
|
|
17
|
-
var version = "0.0.
|
|
17
|
+
var version = "0.0.22";
|
|
18
18
|
//#endregion
|
|
19
19
|
//#region src/cli/commands/command.ts
|
|
20
20
|
function defineCommand(node) {
|
|
@@ -200,6 +200,7 @@ function shouldSkipOptionValue(arg) {
|
|
|
200
200
|
"--rule-concurrency",
|
|
201
201
|
"--since",
|
|
202
202
|
"--timeout-ms",
|
|
203
|
+
"-c",
|
|
203
204
|
"-f",
|
|
204
205
|
"-l"
|
|
205
206
|
].includes(arg);
|
|
@@ -258,7 +259,8 @@ function createProviderId(endpoint, existingIds) {
|
|
|
258
259
|
let base = "provider";
|
|
259
260
|
try {
|
|
260
261
|
const url = new URL(endpoint);
|
|
261
|
-
|
|
262
|
+
const source = findProviderSetupSourceByEndpoint(url);
|
|
263
|
+
base = normalizeProviderIdBase(source?.defaultProviderId ?? source?.label ?? url.hostname);
|
|
262
264
|
} catch {
|
|
263
265
|
base = "provider";
|
|
264
266
|
}
|
|
@@ -362,6 +364,9 @@ function formatTable(rows) {
|
|
|
362
364
|
drawHorizontalLine: () => false
|
|
363
365
|
});
|
|
364
366
|
}
|
|
367
|
+
function normalizeProviderIdBase(value) {
|
|
368
|
+
return value.replace(/[^a-z0-9]+/gi, "-").replace(/^-|-$/g, "").toLowerCase() || "provider";
|
|
369
|
+
}
|
|
365
370
|
//#endregion
|
|
366
371
|
//#region src/cli/commands/config/setup-config.ts
|
|
367
372
|
async function loadMergedSetupConfig(io) {
|
|
@@ -1715,6 +1720,53 @@ const plugin = defineCommand({
|
|
|
1715
1720
|
//#region src/cli/commands/setup/interactive.ts
|
|
1716
1721
|
const nonTtyMessage = "interactive setup requires a TTY. Use -N/--no-interactive with --provider-id and --provider-endpoint.\n";
|
|
1717
1722
|
const backValue = "__alint_back__";
|
|
1723
|
+
function applyDefaultAlias(config, target) {
|
|
1724
|
+
return {
|
|
1725
|
+
...config,
|
|
1726
|
+
providers: config.providers.map((provider) => ({
|
|
1727
|
+
...provider,
|
|
1728
|
+
headers: provider.headers === void 0 ? void 0 : { ...provider.headers },
|
|
1729
|
+
models: provider.models.map((model) => {
|
|
1730
|
+
const aliasesWithoutDefault = (model.aliases ?? []).filter((alias) => alias !== "default");
|
|
1731
|
+
const aliases = provider.id === target.providerId && model.id === target.modelId ? [...aliasesWithoutDefault, "default"] : aliasesWithoutDefault;
|
|
1732
|
+
return {
|
|
1733
|
+
...model,
|
|
1734
|
+
aliases: aliases.length > 0 ? aliases : void 0,
|
|
1735
|
+
capabilities: model.capabilities === void 0 ? void 0 : [...model.capabilities],
|
|
1736
|
+
defaultParams: model.defaultParams === void 0 ? void 0 : { ...model.defaultParams }
|
|
1737
|
+
};
|
|
1738
|
+
})
|
|
1739
|
+
}))
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
function createDefaultModelCandidates(config, newProviderId, newModelIds) {
|
|
1743
|
+
const candidates = [];
|
|
1744
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1745
|
+
const newModelIdSet = new Set(newModelIds);
|
|
1746
|
+
const allModels = config.providers.flatMap((provider) => provider.models.map((model) => ({
|
|
1747
|
+
isCurrentDefault: (model.aliases ?? []).includes("default"),
|
|
1748
|
+
isNew: provider.id === newProviderId && newModelIdSet.has(model.id),
|
|
1749
|
+
model,
|
|
1750
|
+
provider,
|
|
1751
|
+
value: createDefaultModelCandidateValue(provider.id, model.id)
|
|
1752
|
+
})));
|
|
1753
|
+
const addCandidate = (candidate) => {
|
|
1754
|
+
if (candidate === void 0 || seen.has(candidate.value)) return;
|
|
1755
|
+
seen.add(candidate.value);
|
|
1756
|
+
candidates.push({
|
|
1757
|
+
isCurrentDefault: candidate.isCurrentDefault,
|
|
1758
|
+
isNew: candidate.isNew,
|
|
1759
|
+
label: `${candidate.provider.id} / ${candidate.model.id}`,
|
|
1760
|
+
modelId: candidate.model.id,
|
|
1761
|
+
providerId: candidate.provider.id,
|
|
1762
|
+
value: candidate.value
|
|
1763
|
+
});
|
|
1764
|
+
};
|
|
1765
|
+
addCandidate(allModels.find((candidate) => candidate.isCurrentDefault));
|
|
1766
|
+
for (const modelId of newModelIds) addCandidate(allModels.find((candidate) => candidate.provider.id === newProviderId && candidate.model.id === modelId));
|
|
1767
|
+
for (const candidate of allModels) addCandidate(candidate);
|
|
1768
|
+
return candidates;
|
|
1769
|
+
}
|
|
1718
1770
|
function formatProbeModelsFailure(endpoint, error) {
|
|
1719
1771
|
const hint = endpoint.startsWith("https://localhost:11434") ? " Ollama usually uses http://localhost:11434/v1." : "";
|
|
1720
1772
|
return `Could not probe models: ${errorMessageFrom(error)}.${hint}`;
|
|
@@ -1786,9 +1838,8 @@ async function runInteractiveSetup(io) {
|
|
|
1786
1838
|
if (step === "providerId") {
|
|
1787
1839
|
const existingConfig = await loadSetupConfig(getConfigPath(io, draft.scope ?? "global"));
|
|
1788
1840
|
const providerId = await prompts.text({
|
|
1789
|
-
|
|
1790
|
-
message: "Provider id",
|
|
1791
|
-
placeholder: "Type .. to go back",
|
|
1841
|
+
initialValue: draft.providerId ?? createProviderId(draft.endpoint ?? "", new Set(existingConfig.providers.map((provider) => provider.id))),
|
|
1842
|
+
message: "Provider id (type .. to go back)",
|
|
1792
1843
|
validate: (value) => isBackInput(value ?? "") || (value ?? "").trim().length > 0 ? void 0 : "Provider id is required."
|
|
1793
1844
|
});
|
|
1794
1845
|
if (prompts.isCancel(providerId)) return cancelPrompt();
|
|
@@ -1803,9 +1854,9 @@ async function runInteractiveSetup(io) {
|
|
|
1803
1854
|
}
|
|
1804
1855
|
if (step === "headers") {
|
|
1805
1856
|
const headerInput = await prompts.text({
|
|
1806
|
-
|
|
1807
|
-
message: "Headers",
|
|
1808
|
-
placeholder: "Authorization=Bearer token, X-Test=true
|
|
1857
|
+
initialValue: draft.headerInput,
|
|
1858
|
+
message: "Headers (leave empty to skip; type .. to go back)",
|
|
1859
|
+
placeholder: "Authorization=Bearer token, X-Test=true",
|
|
1809
1860
|
validate: (value) => {
|
|
1810
1861
|
if (isBackInput(value ?? "")) return;
|
|
1811
1862
|
try {
|
|
@@ -1841,26 +1892,69 @@ async function runInteractiveSetup(io) {
|
|
|
1841
1892
|
continue;
|
|
1842
1893
|
}
|
|
1843
1894
|
if (step === "defaultAlias") {
|
|
1844
|
-
const
|
|
1895
|
+
const defaultAliasAction = await prompts.select({
|
|
1845
1896
|
message: `Add alias "default" to ${draft.selectedModels?.[0]}?`,
|
|
1846
|
-
options: withBackOption([
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1897
|
+
options: withBackOption([
|
|
1898
|
+
{
|
|
1899
|
+
label: "Yes",
|
|
1900
|
+
value: "yes"
|
|
1901
|
+
},
|
|
1902
|
+
{
|
|
1903
|
+
label: "No",
|
|
1904
|
+
value: "no"
|
|
1905
|
+
},
|
|
1906
|
+
{
|
|
1907
|
+
label: "Select another",
|
|
1908
|
+
value: "selectAnother"
|
|
1909
|
+
}
|
|
1910
|
+
])
|
|
1853
1911
|
});
|
|
1854
|
-
if (prompts.isCancel(
|
|
1855
|
-
if (
|
|
1912
|
+
if (prompts.isCancel(defaultAliasAction)) return cancelPrompt();
|
|
1913
|
+
if (defaultAliasAction === backValue) {
|
|
1856
1914
|
step = "models";
|
|
1857
1915
|
continue;
|
|
1858
1916
|
}
|
|
1859
|
-
|
|
1917
|
+
if (defaultAliasAction === "selectAnother") {
|
|
1918
|
+
step = "selectDefaultModel";
|
|
1919
|
+
continue;
|
|
1920
|
+
}
|
|
1921
|
+
draft.defaultAliasTarget = defaultAliasAction === "yes" ? {
|
|
1922
|
+
modelId: draft.selectedModels?.[0] ?? "",
|
|
1923
|
+
providerId: (draft.providerId ?? "").trim()
|
|
1924
|
+
} : void 0;
|
|
1925
|
+
step = "confirm";
|
|
1926
|
+
continue;
|
|
1927
|
+
}
|
|
1928
|
+
if (step === "selectDefaultModel") {
|
|
1929
|
+
const existingConfig = await loadSetupConfig(getConfigPath(io, draft.scope ?? "global"));
|
|
1930
|
+
const draftProvider = createProviderConfig((draft.providerId ?? "").trim(), (draft.endpoint ?? "").trim(), draft.headers, draft.selectedModels ?? []);
|
|
1931
|
+
const candidates = createDefaultModelCandidates(mergeSetupConfigs(existingConfig, {
|
|
1932
|
+
providers: [draftProvider],
|
|
1933
|
+
version: 1
|
|
1934
|
+
}), draftProvider.id, draft.selectedModels ?? []);
|
|
1935
|
+
const defaultModel = await prompts.select({
|
|
1936
|
+
message: "Select default model",
|
|
1937
|
+
options: withBackOption(candidates.map((candidate) => ({
|
|
1938
|
+
hint: candidate.isCurrentDefault ? "current default" : candidate.isNew ? "new" : void 0,
|
|
1939
|
+
label: candidate.label,
|
|
1940
|
+
value: candidate.value
|
|
1941
|
+
})))
|
|
1942
|
+
});
|
|
1943
|
+
if (prompts.isCancel(defaultModel)) return cancelPrompt();
|
|
1944
|
+
if (defaultModel === backValue) {
|
|
1945
|
+
step = "defaultAlias";
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
const candidate = candidates.find((item) => item.value === defaultModel);
|
|
1949
|
+
if (candidate === void 0) return cancelPrompt();
|
|
1950
|
+
draft.defaultAliasTarget = {
|
|
1951
|
+
modelId: candidate.modelId,
|
|
1952
|
+
providerId: candidate.providerId
|
|
1953
|
+
};
|
|
1860
1954
|
step = "confirm";
|
|
1861
1955
|
continue;
|
|
1862
1956
|
}
|
|
1863
|
-
const nextProvider = createProviderConfig((draft.providerId ?? "").trim(), (draft.endpoint ?? "").trim(), draft.headers, draft.selectedModels ?? []
|
|
1957
|
+
const nextProvider = createProviderConfig((draft.providerId ?? "").trim(), (draft.endpoint ?? "").trim(), draft.headers, draft.selectedModels ?? []);
|
|
1864
1958
|
const confirmed = await prompts.select({
|
|
1865
1959
|
message: [
|
|
1866
1960
|
`Write ${draft.scope} setup config?`,
|
|
@@ -1883,10 +1977,11 @@ async function runInteractiveSetup(io) {
|
|
|
1883
1977
|
}
|
|
1884
1978
|
if (confirmed === "no") return cancelPrompt();
|
|
1885
1979
|
const configPath = getConfigPath(io, draft.scope ?? "global");
|
|
1886
|
-
|
|
1980
|
+
const mergedConfig = mergeSetupConfigs(await loadSetupConfig(configPath), {
|
|
1887
1981
|
providers: [nextProvider],
|
|
1888
1982
|
version: 1
|
|
1889
|
-
})
|
|
1983
|
+
});
|
|
1984
|
+
await writeSetupConfig(configPath, draft.defaultAliasTarget === void 0 ? mergedConfig : applyDefaultAlias(mergedConfig, draft.defaultAliasTarget));
|
|
1890
1985
|
prompts.outro(`Wrote ${configPath}`);
|
|
1891
1986
|
return 0;
|
|
1892
1987
|
}
|
|
@@ -1897,13 +1992,15 @@ function withBackOption(options) {
|
|
|
1897
1992
|
value: backValue
|
|
1898
1993
|
}];
|
|
1899
1994
|
}
|
|
1900
|
-
function
|
|
1995
|
+
function createDefaultModelCandidateValue(providerId, modelId) {
|
|
1996
|
+
return `${providerId}\u0000${modelId}`;
|
|
1997
|
+
}
|
|
1998
|
+
function createProviderConfig(providerId, endpoint, headers, modelIds) {
|
|
1901
1999
|
return {
|
|
1902
2000
|
endpoint,
|
|
1903
2001
|
headers,
|
|
1904
2002
|
id: providerId,
|
|
1905
|
-
models: modelIds.map((modelId
|
|
1906
|
-
aliases: index === 0 && addDefaultAlias ? ["default"] : void 0,
|
|
2003
|
+
models: modelIds.map((modelId) => ({
|
|
1907
2004
|
id: modelId,
|
|
1908
2005
|
name: modelId
|
|
1909
2006
|
})),
|
|
@@ -1927,9 +2024,9 @@ async function probeModelsWithSpinner(prompts, endpoint, headers) {
|
|
|
1927
2024
|
}
|
|
1928
2025
|
async function promptEndpoint(prompts, source) {
|
|
1929
2026
|
return prompts.text({
|
|
1930
|
-
|
|
1931
|
-
message: "Provider endpoint",
|
|
1932
|
-
placeholder:
|
|
2027
|
+
initialValue: source.defaultEndpoint,
|
|
2028
|
+
message: "Provider endpoint (type .. to go back)",
|
|
2029
|
+
placeholder: source.defaultEndpoint ?? "https://example.test/v1",
|
|
1933
2030
|
validate: (value) => isBackInput(value ?? "") || (value ?? "").trim().length > 0 ? void 0 : "Provider endpoint is required."
|
|
1934
2031
|
});
|
|
1935
2032
|
}
|
|
@@ -2163,7 +2260,7 @@ async function executeCli(argv, io) {
|
|
|
2163
2260
|
pendingResult = result;
|
|
2164
2261
|
return result;
|
|
2165
2262
|
};
|
|
2166
|
-
cli.option("--no-cache", "Disable cache for this run").option("--cache-location <path>", "Path to the alint cache file or directory").option("--config <path>", "Path to alint config file").option("--file-concurrency <count>", "Number of files to lint concurrently").option("--format <format>", "Reporter format", { default: "stylish" }).option("--model <model>", "Force a model override").option("-l, --lang <language>", "Ask model-backed rules to write diagnostics in this language").option("--progress", "Show run progress").option("--rule-concurrency <count>", "Number of rules to run concurrently within a file").option("--no-stats", "Do not record run stats for this run").option("--timeout-ms <ms>", "Rule execution timeout in milliseconds").version(version).help();
|
|
2263
|
+
cli.option("--no-cache", "Disable cache for this run").option("--cache-location <path>", "Path to the alint cache file or directory").option("-c, --config <path>", "Path to alint config file").option("--file-concurrency <count>", "Number of files to lint concurrently").option("--format <format>", "Reporter format", { default: "stylish" }).option("--model <model>", "Force a model override").option("-l, --lang <language>", "Ask model-backed rules to write diagnostics in this language").option("--progress", "Show run progress").option("--rule-concurrency <count>", "Number of rules to run concurrently within a file").option("--no-stats", "Do not record run stats for this run").option("--timeout-ms <ms>", "Rule execution timeout in milliseconds").version(version).help();
|
|
2167
2264
|
registerCommandTree(cli, commandTree, {
|
|
2168
2265
|
globalOptions,
|
|
2169
2266
|
interceptConsoleOutput,
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as formatJson, n as formatDiagnostics, r as formatStylish, t as executeCli } from "./cli-
|
|
1
|
+
import { i as formatJson, n as formatDiagnostics, r as formatStylish, t as executeCli } from "./cli-CmLooFry.mjs";
|
|
2
2
|
import { ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated } from "@alint-js/config";
|
|
3
3
|
import { defineConfig } from "@alint-js/core";
|
|
4
4
|
export { defineConfig, executeCli, formatDiagnostics, formatJson, formatStylish, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alint-js/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.22",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.mts",
|
|
@@ -25,18 +25,14 @@
|
|
|
25
25
|
"pathe": "^2.0.3",
|
|
26
26
|
"table": "^6.9.0",
|
|
27
27
|
"tinyrainbow": "^3.1.0",
|
|
28
|
-
"@alint-js/config": "0.0.
|
|
29
|
-
"@alint-js/core": "0.0.
|
|
28
|
+
"@alint-js/config": "0.0.22",
|
|
29
|
+
"@alint-js/core": "0.0.22"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@pnpm/find-workspace-dir": "^1000.1.5",
|
|
33
|
-
"@types/node": "^26.0.1",
|
|
34
33
|
"local-pkg": "^1.2.1",
|
|
35
34
|
"mlly": "^1.8.2",
|
|
36
|
-
"tinyexec": "^1.2.4"
|
|
37
|
-
"tsdown": "^0.22.3",
|
|
38
|
-
"typescript": "^6.0.3",
|
|
39
|
-
"vitest": "^4.1.9"
|
|
35
|
+
"tinyexec": "^1.2.4"
|
|
40
36
|
},
|
|
41
37
|
"scripts": {
|
|
42
38
|
"alint": "node ./dist/bin/index.mjs",
|