notoken-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/config/file-hints.json +255 -0
  2. package/config/hosts.json +14 -0
  3. package/config/intents.json +3920 -0
  4. package/config/playbooks.json +112 -0
  5. package/config/rules.json +100 -0
  6. package/dist/agents/agentSpawner.d.ts +56 -0
  7. package/dist/agents/agentSpawner.js +180 -0
  8. package/dist/agents/planner.d.ts +40 -0
  9. package/dist/agents/planner.js +175 -0
  10. package/dist/agents/playbookRunner.d.ts +45 -0
  11. package/dist/agents/playbookRunner.js +120 -0
  12. package/dist/agents/taskRunner.d.ts +61 -0
  13. package/dist/agents/taskRunner.js +142 -0
  14. package/dist/context/history.d.ts +36 -0
  15. package/dist/context/history.js +115 -0
  16. package/dist/conversation/coreference.d.ts +27 -0
  17. package/dist/conversation/coreference.js +147 -0
  18. package/dist/conversation/secrets.d.ts +43 -0
  19. package/dist/conversation/secrets.js +129 -0
  20. package/dist/conversation/store.d.ts +94 -0
  21. package/dist/conversation/store.js +184 -0
  22. package/dist/execution/git.d.ts +11 -0
  23. package/dist/execution/git.js +146 -0
  24. package/dist/execution/ssh.d.ts +2 -0
  25. package/dist/execution/ssh.js +17 -0
  26. package/dist/handlers/executor.d.ts +8 -0
  27. package/dist/handlers/executor.js +216 -0
  28. package/dist/healing/claudeHealer.d.ts +17 -0
  29. package/dist/healing/claudeHealer.js +300 -0
  30. package/dist/healing/patchPromoter.d.ts +25 -0
  31. package/dist/healing/patchPromoter.js +118 -0
  32. package/dist/healing/ruleBuilder.d.ts +5 -0
  33. package/dist/healing/ruleBuilder.js +111 -0
  34. package/dist/healing/ruleRepairer.d.ts +8 -0
  35. package/dist/healing/ruleRepairer.js +29 -0
  36. package/dist/healing/ruleValidator.d.ts +22 -0
  37. package/dist/healing/ruleValidator.js +145 -0
  38. package/dist/healing/runHealer.d.ts +11 -0
  39. package/dist/healing/runHealer.js +74 -0
  40. package/dist/index.d.ts +51 -0
  41. package/dist/index.js +62 -0
  42. package/dist/intents/catalog.d.ts +4 -0
  43. package/dist/intents/catalog.js +7 -0
  44. package/dist/nlp/disambiguate.d.ts +2 -0
  45. package/dist/nlp/disambiguate.js +46 -0
  46. package/dist/nlp/fuzzyResolver.d.ts +14 -0
  47. package/dist/nlp/fuzzyResolver.js +108 -0
  48. package/dist/nlp/llmFallback.d.ts +63 -0
  49. package/dist/nlp/llmFallback.js +338 -0
  50. package/dist/nlp/llmParser.d.ts +8 -0
  51. package/dist/nlp/llmParser.js +118 -0
  52. package/dist/nlp/multiClassifier.d.ts +39 -0
  53. package/dist/nlp/multiClassifier.js +181 -0
  54. package/dist/nlp/parseIntent.d.ts +2 -0
  55. package/dist/nlp/parseIntent.js +34 -0
  56. package/dist/nlp/ruleParser.d.ts +2 -0
  57. package/dist/nlp/ruleParser.js +234 -0
  58. package/dist/nlp/semantic.d.ts +104 -0
  59. package/dist/nlp/semantic.js +419 -0
  60. package/dist/nlp/uncertainty.d.ts +42 -0
  61. package/dist/nlp/uncertainty.js +103 -0
  62. package/dist/parsers/apacheParser.d.ts +50 -0
  63. package/dist/parsers/apacheParser.js +152 -0
  64. package/dist/parsers/bindParser.d.ts +40 -0
  65. package/dist/parsers/bindParser.js +189 -0
  66. package/dist/parsers/envFile.d.ts +39 -0
  67. package/dist/parsers/envFile.js +128 -0
  68. package/dist/parsers/fileFinder.d.ts +30 -0
  69. package/dist/parsers/fileFinder.js +226 -0
  70. package/dist/parsers/index.d.ts +27 -0
  71. package/dist/parsers/index.js +193 -0
  72. package/dist/parsers/jsonParser.d.ts +16 -0
  73. package/dist/parsers/jsonParser.js +57 -0
  74. package/dist/parsers/nginxParser.d.ts +47 -0
  75. package/dist/parsers/nginxParser.js +161 -0
  76. package/dist/parsers/passwd.d.ts +25 -0
  77. package/dist/parsers/passwd.js +41 -0
  78. package/dist/parsers/shadow.d.ts +23 -0
  79. package/dist/parsers/shadow.js +50 -0
  80. package/dist/parsers/yamlParser.d.ts +13 -0
  81. package/dist/parsers/yamlParser.js +54 -0
  82. package/dist/policy/confirm.d.ts +2 -0
  83. package/dist/policy/confirm.js +29 -0
  84. package/dist/policy/safety.d.ts +4 -0
  85. package/dist/policy/safety.js +32 -0
  86. package/dist/types/intent.d.ts +205 -0
  87. package/dist/types/intent.js +32 -0
  88. package/dist/types/rules.d.ts +237 -0
  89. package/dist/types/rules.js +50 -0
  90. package/dist/utils/analysis.d.ts +25 -0
  91. package/dist/utils/analysis.js +307 -0
  92. package/dist/utils/autoBackup.d.ts +43 -0
  93. package/dist/utils/autoBackup.js +144 -0
  94. package/dist/utils/config.d.ts +11 -0
  95. package/dist/utils/config.js +32 -0
  96. package/dist/utils/dirAnalysis.d.ts +23 -0
  97. package/dist/utils/dirAnalysis.js +192 -0
  98. package/dist/utils/explain.d.ts +8 -0
  99. package/dist/utils/explain.js +145 -0
  100. package/dist/utils/logger.d.ts +5 -0
  101. package/dist/utils/logger.js +29 -0
  102. package/dist/utils/output.d.ts +2 -0
  103. package/dist/utils/output.js +26 -0
  104. package/dist/utils/paths.d.ts +26 -0
  105. package/dist/utils/paths.js +47 -0
  106. package/dist/utils/permissions.d.ts +64 -0
  107. package/dist/utils/permissions.js +298 -0
  108. package/dist/utils/platform.d.ts +53 -0
  109. package/dist/utils/platform.js +253 -0
  110. package/dist/utils/smartFile.d.ts +29 -0
  111. package/dist/utils/smartFile.js +188 -0
  112. package/dist/utils/spinner.d.ts +53 -0
  113. package/dist/utils/spinner.js +140 -0
  114. package/dist/utils/verbose.d.ts +27 -0
  115. package/dist/utils/verbose.js +131 -0
  116. package/dist/utils/wslPaths.d.ts +31 -0
  117. package/dist/utils/wslPaths.js +145 -0
  118. package/package.json +39 -0
@@ -0,0 +1,111 @@
1
+ import { loadRules, loadIntents } from "../utils/config.js";
2
+ import { RulePatch as RulePatchSchema } from "../types/rules.js";
3
+ /**
4
+ * RuleBuilder: asks an LLM to propose new rules from a set of example phrases.
5
+ */
6
+ export async function buildRulesFromExamples(examples) {
7
+ const endpoint = process.env.MYCLI_LLM_ENDPOINT;
8
+ if (!endpoint) {
9
+ console.error("Set MYCLI_LLM_ENDPOINT to use the RuleBuilder.");
10
+ return null;
11
+ }
12
+ const rules = loadRules();
13
+ const intents = loadIntents();
14
+ const apiKey = process.env.MYCLI_LLM_API_KEY ?? "";
15
+ const intentList = intents.map((i) => `- ${i.name}: ${i.description}`).join("\n");
16
+ const prompt = `You are a rule builder for a CLI command parser.
17
+
18
+ Current rules config:
19
+ ${JSON.stringify(rules, null, 2)}
20
+
21
+ Supported intents (from intents.json):
22
+ ${intentList}
23
+
24
+ The following user phrases were NOT understood by the current parser:
25
+ ${examples.map((e) => `- "${e}"`).join("\n")}
26
+
27
+ Analyze each phrase and propose a structured patch to expand the rules.
28
+
29
+ Return a JSON object with this exact schema:
30
+ {
31
+ "summary": "what this patch does",
32
+ "confidence": 0.0-1.0,
33
+ "changes": [
34
+ { "type": "add_intent_synonym", "intent": "...", "phrase": "..." },
35
+ { "type": "add_env_alias", "canonical": "...", "alias": "..." },
36
+ { "type": "add_service_alias", "canonical": "...", "alias": "..." }
37
+ ],
38
+ "tests": [
39
+ { "input": "...", "expectedIntent": "...", "expectedFields": {} },
40
+ { "input": "...", "shouldReject": true }
41
+ ],
42
+ "warnings": ["any overlap or risk concerns"]
43
+ }
44
+
45
+ Rules:
46
+ - Only add synonyms/aliases that clearly map to existing intents or entities.
47
+ - Include at least one positive and one negative test per change.
48
+ - Warn if a new synonym could overlap with another intent.
49
+ - Do NOT invent new intents.
50
+ - Return ONLY the JSON object.`;
51
+ try {
52
+ const response = await fetch(endpoint, {
53
+ method: "POST",
54
+ headers: {
55
+ "Content-Type": "application/json",
56
+ ...(apiKey ? { Authorization: `Bearer ${apiKey}`, "x-api-key": apiKey } : {}),
57
+ },
58
+ body: JSON.stringify({
59
+ model: process.env.MYCLI_LLM_MODEL ?? "claude-sonnet-4-20250514",
60
+ max_tokens: 1024,
61
+ messages: [{ role: "user", content: prompt }],
62
+ }),
63
+ });
64
+ if (!response.ok)
65
+ return null;
66
+ const data = (await response.json());
67
+ const content = extractContent(data);
68
+ if (!content)
69
+ return null;
70
+ const json = extractJSON(content);
71
+ if (!json)
72
+ return null;
73
+ const parsed = RulePatchSchema.safeParse(json);
74
+ if (!parsed.success) {
75
+ console.error("LLM returned invalid patch:", parsed.error.message);
76
+ return null;
77
+ }
78
+ return parsed.data;
79
+ }
80
+ catch (err) {
81
+ console.error("RuleBuilder error:", err);
82
+ return null;
83
+ }
84
+ }
85
+ function extractContent(data) {
86
+ if (data.choices && Array.isArray(data.choices)) {
87
+ const msg = data.choices[0]?.message;
88
+ if (msg?.content && typeof msg.content === "string")
89
+ return msg.content;
90
+ }
91
+ if (data.content && Array.isArray(data.content)) {
92
+ const block = data.content[0];
93
+ if (block?.text && typeof block.text === "string")
94
+ return block.text;
95
+ }
96
+ return null;
97
+ }
98
+ function extractJSON(text) {
99
+ try {
100
+ return JSON.parse(text.trim());
101
+ }
102
+ catch { }
103
+ const match = text.match(/```(?:json)?\s*([\s\S]*?)```/);
104
+ if (match) {
105
+ try {
106
+ return JSON.parse(match[1].trim());
107
+ }
108
+ catch { }
109
+ }
110
+ return null;
111
+ }
@@ -0,0 +1,8 @@
1
+ import type { RulePatch } from "../types/rules.js";
2
+ /**
3
+ * RuleRepairer: reads the failure log and proposes patches to fix unmatched inputs.
4
+ *
5
+ * It batches recent failures, deduplicates, and sends them to the RuleBuilder
6
+ * which uses an LLM to propose structured changes.
7
+ */
8
+ export declare function repairFromFailures(maxFailures?: number): Promise<RulePatch | null>;
@@ -0,0 +1,29 @@
1
+ import { loadFailures } from "../utils/logger.js";
2
+ import { buildRulesFromExamples } from "./ruleBuilder.js";
3
+ /**
4
+ * RuleRepairer: reads the failure log and proposes patches to fix unmatched inputs.
5
+ *
6
+ * It batches recent failures, deduplicates, and sends them to the RuleBuilder
7
+ * which uses an LLM to propose structured changes.
8
+ */
9
+ export async function repairFromFailures(maxFailures = 20) {
10
+ const failures = loadFailures();
11
+ if (failures.length === 0) {
12
+ console.log("No failures to repair.");
13
+ return null;
14
+ }
15
+ // Deduplicate by rawText, take the most recent N
16
+ const unique = deduplicateFailures(failures);
17
+ const batch = unique.slice(-maxFailures);
18
+ console.log(`Repairing from ${batch.length} unique failure(s)...`);
19
+ const examples = batch.map((f) => f.rawText);
20
+ return buildRulesFromExamples(examples);
21
+ }
22
+ function deduplicateFailures(failures) {
23
+ const seen = new Map();
24
+ for (const f of failures) {
25
+ const key = f.rawText.trim().toLowerCase();
26
+ seen.set(key, f); // keep latest
27
+ }
28
+ return Array.from(seen.values());
29
+ }
@@ -0,0 +1,22 @@
1
+ import type { RulePatch } from "../types/rules.js";
2
+ export interface ValidationResult {
3
+ valid: boolean;
4
+ errors: string[];
5
+ warnings: string[];
6
+ testResults: Array<{
7
+ input: string;
8
+ passed: boolean;
9
+ reason?: string;
10
+ }>;
11
+ }
12
+ /**
13
+ * RuleValidator: checks a proposed patch for safety before promotion.
14
+ *
15
+ * Validates:
16
+ * - No overlapping synonyms across different intents
17
+ * - All referenced intents exist
18
+ * - All referenced environments/services exist
19
+ * - Test cases pass against the patched rule set
20
+ * - No dangerous broadening of high-risk intents
21
+ */
22
+ export declare function validatePatch(patch: RulePatch): ValidationResult;
@@ -0,0 +1,145 @@
1
+ import { loadRules, loadIntents } from "../utils/config.js";
2
+ import { parseByRules } from "../nlp/ruleParser.js";
3
+ /**
4
+ * RuleValidator: checks a proposed patch for safety before promotion.
5
+ *
6
+ * Validates:
7
+ * - No overlapping synonyms across different intents
8
+ * - All referenced intents exist
9
+ * - All referenced environments/services exist
10
+ * - Test cases pass against the patched rule set
11
+ * - No dangerous broadening of high-risk intents
12
+ */
13
+ export function validatePatch(patch) {
14
+ const rules = loadRules();
15
+ const errors = [];
16
+ const warnings = [...patch.warnings];
17
+ // Check each change
18
+ for (const change of patch.changes) {
19
+ switch (change.type) {
20
+ case "add_intent_synonym": {
21
+ const knownIntent = rules.intentSynonyms[change.intent] || loadIntents().some((i) => i.name === change.intent);
22
+ if (!knownIntent) {
23
+ errors.push(`Unknown intent: ${change.intent}`);
24
+ }
25
+ // Check for overlap with other intents
26
+ const overlap = findSynonymOverlap(change.phrase, change.intent, rules);
27
+ if (overlap) {
28
+ errors.push(`Synonym "${change.phrase}" overlaps with intent "${overlap}"`);
29
+ }
30
+ // Check for overly broad synonyms
31
+ if (change.phrase.length <= 2) {
32
+ errors.push(`Synonym "${change.phrase}" is too short / too broad`);
33
+ }
34
+ break;
35
+ }
36
+ case "add_env_alias": {
37
+ if (!rules.environmentAliases[change.canonical]) {
38
+ errors.push(`Unknown environment: ${change.canonical}`);
39
+ }
40
+ break;
41
+ }
42
+ case "add_service_alias": {
43
+ if (!rules.serviceAliases[change.canonical]) {
44
+ errors.push(`Unknown service: ${change.canonical}`);
45
+ }
46
+ break;
47
+ }
48
+ case "remove_intent_synonym": {
49
+ const existing = rules.intentSynonyms[change.intent];
50
+ if (!existing?.includes(change.phrase)) {
51
+ warnings.push(`Synonym "${change.phrase}" not found in intent "${change.intent}"`);
52
+ }
53
+ break;
54
+ }
55
+ }
56
+ }
57
+ // Simulate applying the patch and run tests
58
+ const patchedRules = simulateApply(rules, patch);
59
+ const testResults = runTests(patch, patchedRules);
60
+ const failedTests = testResults.filter((t) => !t.passed);
61
+ if (failedTests.length > 0) {
62
+ errors.push(`${failedTests.length} test(s) failed`);
63
+ }
64
+ return {
65
+ valid: errors.length === 0,
66
+ errors,
67
+ warnings,
68
+ testResults,
69
+ };
70
+ }
71
+ function findSynonymOverlap(phrase, excludeIntent, rules) {
72
+ // Check rules.json synonyms
73
+ for (const [intent, phrases] of Object.entries(rules.intentSynonyms)) {
74
+ if (intent === excludeIntent)
75
+ continue;
76
+ if (phrases.includes(phrase))
77
+ return intent;
78
+ }
79
+ // Also check intents.json synonyms (primary source now)
80
+ for (const def of loadIntents()) {
81
+ if (def.name === excludeIntent)
82
+ continue;
83
+ if (def.synonyms.includes(phrase))
84
+ return def.name;
85
+ }
86
+ return null;
87
+ }
88
+ function simulateApply(rules, patch) {
89
+ const clone = JSON.parse(JSON.stringify(rules));
90
+ for (const change of patch.changes) {
91
+ switch (change.type) {
92
+ case "add_intent_synonym":
93
+ if (clone.intentSynonyms[change.intent]) {
94
+ clone.intentSynonyms[change.intent].push(change.phrase);
95
+ }
96
+ break;
97
+ case "add_env_alias":
98
+ if (clone.environmentAliases[change.canonical]) {
99
+ clone.environmentAliases[change.canonical].push(change.alias);
100
+ }
101
+ break;
102
+ case "add_service_alias":
103
+ if (clone.serviceAliases[change.canonical]) {
104
+ clone.serviceAliases[change.canonical].push(change.alias);
105
+ }
106
+ break;
107
+ case "remove_intent_synonym":
108
+ if (clone.intentSynonyms[change.intent]) {
109
+ clone.intentSynonyms[change.intent] = clone.intentSynonyms[change.intent].filter((p) => p !== change.phrase);
110
+ }
111
+ break;
112
+ }
113
+ }
114
+ return clone;
115
+ }
116
+ function runTests(patch, _patchedRules) {
117
+ // We test against current loaded rules + patch applied
118
+ // For now, we use parseByRules which reads from loadRules()
119
+ // In a full implementation, you'd inject the patched rules
120
+ return patch.tests.map((test) => {
121
+ const result = parseByRules(test.input);
122
+ if (test.shouldReject) {
123
+ return {
124
+ input: test.input,
125
+ passed: result === null || result.intent === "unknown",
126
+ reason: result ? `Matched as ${result.intent} but should reject` : undefined,
127
+ };
128
+ }
129
+ if (!result) {
130
+ return {
131
+ input: test.input,
132
+ passed: false,
133
+ reason: "No parse result",
134
+ };
135
+ }
136
+ if (test.expectedIntent && result.intent !== test.expectedIntent) {
137
+ return {
138
+ input: test.input,
139
+ passed: false,
140
+ reason: `Expected ${test.expectedIntent}, got ${result.intent}`,
141
+ };
142
+ }
143
+ return { input: test.input, passed: true };
144
+ });
145
+ }
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Standalone healer script.
4
+ *
5
+ * Reads the failure log, asks the LLM to propose fixes, validates,
6
+ * and optionally promotes the patch.
7
+ *
8
+ * Usage:
9
+ * npx tsx src/healing/runHealer.ts [--promote] [--force] [--dry-run]
10
+ */
11
+ export {};
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Standalone healer script.
4
+ *
5
+ * Reads the failure log, asks the LLM to propose fixes, validates,
6
+ * and optionally promotes the patch.
7
+ *
8
+ * Usage:
9
+ * npx tsx src/healing/runHealer.ts [--promote] [--force] [--dry-run]
10
+ */
11
+ import { repairFromFailures } from "./ruleRepairer.js";
12
+ import { validatePatch } from "./ruleValidator.js";
13
+ import { promotePatch } from "./patchPromoter.js";
14
+ import { clearFailures } from "../utils/logger.js";
15
+ async function main() {
16
+ const args = process.argv.slice(2);
17
+ const shouldPromote = args.includes("--promote");
18
+ const force = args.includes("--force");
19
+ const dryRun = args.includes("--dry-run");
20
+ console.log("=== Auto-Learning Rule Repairer ===\n");
21
+ const patch = await repairFromFailures();
22
+ if (!patch) {
23
+ console.log("No patch generated.");
24
+ return;
25
+ }
26
+ console.log("\n--- Proposed Patch ---");
27
+ console.log(`Summary: ${patch.summary}`);
28
+ console.log(`Confidence: ${(patch.confidence * 100).toFixed(0)}%`);
29
+ console.log(`Changes: ${patch.changes.length}`);
30
+ for (const c of patch.changes) {
31
+ console.log(` [${c.type}] ${JSON.stringify(c)}`);
32
+ }
33
+ console.log(`\nTests: ${patch.tests.length}`);
34
+ for (const t of patch.tests) {
35
+ const label = t.shouldReject ? "REJECT" : t.expectedIntent ?? "?";
36
+ console.log(` "${t.input}" => ${label}`);
37
+ }
38
+ if (patch.warnings.length > 0) {
39
+ console.log(`\nWarnings:`);
40
+ for (const w of patch.warnings)
41
+ console.log(` - ${w}`);
42
+ }
43
+ console.log("\n--- Validation ---");
44
+ const validation = validatePatch(patch);
45
+ console.log(`Valid: ${validation.valid}`);
46
+ if (validation.errors.length > 0) {
47
+ console.log("Errors:");
48
+ for (const e of validation.errors)
49
+ console.log(` - ${e}`);
50
+ }
51
+ console.log("Test results:");
52
+ for (const t of validation.testResults) {
53
+ console.log(` ${t.passed ? "PASS" : "FAIL"} "${t.input}"${t.reason ? ` (${t.reason})` : ""}`);
54
+ }
55
+ if (shouldPromote) {
56
+ console.log("\n--- Promoting ---");
57
+ const result = promotePatch(patch, { force, dryRun });
58
+ if (result.promoted) {
59
+ console.log("Patch applied successfully.");
60
+ clearFailures();
61
+ console.log("Failure log cleared.");
62
+ }
63
+ else {
64
+ console.log("Patch not promoted.");
65
+ }
66
+ }
67
+ else {
68
+ console.log("\nRun with --promote to apply this patch.");
69
+ }
70
+ }
71
+ main().catch((err) => {
72
+ console.error("Healer error:", err);
73
+ process.exit(1);
74
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * notoken-core — shared library.
3
+ *
4
+ * Exports the core engine that both CLI and Electron app use.
5
+ * This is the single source of truth for all notoken functionality.
6
+ *
7
+ * Usage:
8
+ * import { parse, execute, detect, install, doctor } from "notoken/core";
9
+ */
10
+ export { parseIntent } from "./nlp/parseIntent.js";
11
+ export { parseByRules } from "./nlp/ruleParser.js";
12
+ export { disambiguate } from "./nlp/disambiguate.js";
13
+ export { classifyMulti } from "./nlp/multiClassifier.js";
14
+ export { semanticParse, tokenize, keyboardDistance, fuzzyMatch } from "./nlp/semantic.js";
15
+ export { analyzeUncertainty, getUncoveredSpans } from "./nlp/uncertainty.js";
16
+ export { llmFallback, isLLMConfigured, getLLMBackend, formatLLMFallback } from "./nlp/llmFallback.js";
17
+ export { executeIntent } from "./handlers/executor.js";
18
+ export { runRemoteCommand, runLocalCommand } from "./execution/ssh.js";
19
+ export { loadRules, loadIntents, getIntentDef, loadHosts, getConfigDir } from "./utils/config.js";
20
+ export { CONFIG_DIR, DATA_DIR, LOG_DIR, PACKAGE_ROOT, USER_HOME, isSEA, ensureUserDirs } from "./utils/paths.js";
21
+ export { detectLocalPlatform, getInstallCommand, getServiceCommand, getPackageForCommand, formatPlatform } from "./utils/platform.js";
22
+ export type { PlatformInfo } from "./utils/platform.js";
23
+ export { winToLinux, linuxToWin, normalizePath, getUserDirs, isWSL, isWindows } from "./utils/wslPaths.js";
24
+ export { getLocalPermissions, getRemotePermissions, checkAccessForIntent, parsePermissionRequest, formatPermissionsDisplay } from "./utils/permissions.js";
25
+ export { analyzeOutput, analyzeLoad, analyzeDisk, analyzeMemory } from "./utils/analysis.js";
26
+ export { analyzeDirectory } from "./utils/dirAnalysis.js";
27
+ export { smartRead, smartSearch, getFileInfo } from "./utils/smartFile.js";
28
+ export { parseFile, formatParsedFile, detectFileType } from "./parsers/index.js";
29
+ export { findKnownLocations, searchRemoteFile } from "./parsers/fileFinder.js";
30
+ export { getOrCreateConversation, addUserTurn, addSystemTurn, saveConversation, getLastEntity, getRecentEntities, listConversations } from "./conversation/store.js";
31
+ export type { Conversation, ConversationTurn, KnowledgeNode, UncertaintyReport } from "./conversation/store.js";
32
+ export { resolveCoreferences, extractEntitiesFromFields } from "./conversation/coreference.js";
33
+ export { redactSecrets, listSecrets, clearSecrets, saveSecretsToFile, resolvePlaceholders } from "./conversation/secrets.js";
34
+ export { taskRunner, type BackgroundTask } from "./agents/taskRunner.js";
35
+ export { agentSpawner, type AgentHandle } from "./agents/agentSpawner.js";
36
+ export { createPlan, formatPlan } from "./agents/planner.js";
37
+ export { loadPlaybooks, getPlaybook, runPlaybook, formatPlaybookList } from "./agents/playbookRunner.js";
38
+ export { validatePatch } from "./healing/ruleValidator.js";
39
+ export { promotePatch } from "./healing/patchPromoter.js";
40
+ export { validateIntent, isDangerous, getRiskLevel } from "./policy/safety.js";
41
+ export { askForConfirmation, askForChoice } from "./policy/confirm.js";
42
+ export { formatVerbose, formatTaskNotification, formatJobsList } from "./utils/verbose.js";
43
+ export { formatExplain } from "./utils/explain.js";
44
+ export { formatParsedCommand } from "./utils/output.js";
45
+ export { Spinner, withSpinner, progressBar } from "./utils/spinner.js";
46
+ export { createBackup, rollback, listBackups, cleanExpiredBackups, formatBackupList } from "./utils/autoBackup.js";
47
+ export { logFailure, loadFailures, clearFailures } from "./utils/logger.js";
48
+ export { logUncertainty, loadUncertaintyLog, getUncertaintySummary } from "./nlp/uncertainty.js";
49
+ export { recordHistory, loadHistory, getRecentHistory, searchHistory } from "./context/history.js";
50
+ export type { DynamicIntent, ParsedCommand, IntentDef, FieldDef, EnvironmentName } from "./types/intent.js";
51
+ export type { RulePatch, RulePatchChange, FailureLog, RulesConfig } from "./types/rules.js";
package/dist/index.js ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * notoken-core — shared library.
3
+ *
4
+ * Exports the core engine that both CLI and Electron app use.
5
+ * This is the single source of truth for all notoken functionality.
6
+ *
7
+ * Usage:
8
+ * import { parse, execute, detect, install, doctor } from "notoken/core";
9
+ */
10
+ // ── NLP & Parsing ──
11
+ export { parseIntent } from "./nlp/parseIntent.js";
12
+ export { parseByRules } from "./nlp/ruleParser.js";
13
+ export { disambiguate } from "./nlp/disambiguate.js";
14
+ export { classifyMulti } from "./nlp/multiClassifier.js";
15
+ export { semanticParse, tokenize, keyboardDistance, fuzzyMatch } from "./nlp/semantic.js";
16
+ export { analyzeUncertainty, getUncoveredSpans } from "./nlp/uncertainty.js";
17
+ export { llmFallback, isLLMConfigured, getLLMBackend, formatLLMFallback } from "./nlp/llmFallback.js";
18
+ // ── Execution ──
19
+ export { executeIntent } from "./handlers/executor.js";
20
+ export { runRemoteCommand, runLocalCommand } from "./execution/ssh.js";
21
+ // ── Config ──
22
+ export { loadRules, loadIntents, getIntentDef, loadHosts, getConfigDir } from "./utils/config.js";
23
+ export { CONFIG_DIR, DATA_DIR, LOG_DIR, PACKAGE_ROOT, USER_HOME, isSEA, ensureUserDirs } from "./utils/paths.js";
24
+ // ── Platform & Detection ──
25
+ export { detectLocalPlatform, getInstallCommand, getServiceCommand, getPackageForCommand, formatPlatform } from "./utils/platform.js";
26
+ // ── Paths ──
27
+ export { winToLinux, linuxToWin, normalizePath, getUserDirs, isWSL, isWindows } from "./utils/wslPaths.js";
28
+ // ── Permissions ──
29
+ export { getLocalPermissions, getRemotePermissions, checkAccessForIntent, parsePermissionRequest, formatPermissionsDisplay } from "./utils/permissions.js";
30
+ // ── Analysis ──
31
+ export { analyzeOutput, analyzeLoad, analyzeDisk, analyzeMemory } from "./utils/analysis.js";
32
+ export { analyzeDirectory } from "./utils/dirAnalysis.js";
33
+ // ── File Operations ──
34
+ export { smartRead, smartSearch, getFileInfo } from "./utils/smartFile.js";
35
+ export { parseFile, formatParsedFile, detectFileType } from "./parsers/index.js";
36
+ export { findKnownLocations, searchRemoteFile } from "./parsers/fileFinder.js";
37
+ // ── Conversation ──
38
+ export { getOrCreateConversation, addUserTurn, addSystemTurn, saveConversation, getLastEntity, getRecentEntities, listConversations } from "./conversation/store.js";
39
+ export { resolveCoreferences, extractEntitiesFromFields } from "./conversation/coreference.js";
40
+ export { redactSecrets, listSecrets, clearSecrets, saveSecretsToFile, resolvePlaceholders } from "./conversation/secrets.js";
41
+ // ── Agents & Background ──
42
+ export { taskRunner } from "./agents/taskRunner.js";
43
+ export { agentSpawner } from "./agents/agentSpawner.js";
44
+ export { createPlan, formatPlan } from "./agents/planner.js";
45
+ export { loadPlaybooks, getPlaybook, runPlaybook, formatPlaybookList } from "./agents/playbookRunner.js";
46
+ // ── Healing / Learning ──
47
+ export { validatePatch } from "./healing/ruleValidator.js";
48
+ export { promotePatch } from "./healing/patchPromoter.js";
49
+ // ── Safety ──
50
+ export { validateIntent, isDangerous, getRiskLevel } from "./policy/safety.js";
51
+ export { askForConfirmation, askForChoice } from "./policy/confirm.js";
52
+ // ── UI Helpers ──
53
+ export { formatVerbose, formatTaskNotification, formatJobsList } from "./utils/verbose.js";
54
+ export { formatExplain } from "./utils/explain.js";
55
+ export { formatParsedCommand } from "./utils/output.js";
56
+ export { Spinner, withSpinner, progressBar } from "./utils/spinner.js";
57
+ // ── Auto-backup ──
58
+ export { createBackup, rollback, listBackups, cleanExpiredBackups, formatBackupList } from "./utils/autoBackup.js";
59
+ // ── Logging ──
60
+ export { logFailure, loadFailures, clearFailures } from "./utils/logger.js";
61
+ export { logUncertainty, loadUncertaintyLog, getUncertaintySummary } from "./nlp/uncertainty.js";
62
+ export { recordHistory, loadHistory, getRecentHistory, searchHistory } from "./context/history.js";
@@ -0,0 +1,4 @@
1
+ import type { IntentDef } from "../types/intent.js";
2
+ export type { IntentDef };
3
+ export declare function getIntentCatalog(): IntentDef[];
4
+ export declare function getCatalogEntry(intentName: string): IntentDef | undefined;
@@ -0,0 +1,7 @@
1
+ import { loadIntents, getIntentDef } from "../utils/config.js";
2
+ export function getIntentCatalog() {
3
+ return loadIntents();
4
+ }
5
+ export function getCatalogEntry(intentName) {
6
+ return getIntentDef(intentName);
7
+ }
@@ -0,0 +1,2 @@
1
+ import type { DynamicIntent, ParsedCommand } from "../types/intent.js";
2
+ export declare function disambiguate(intent: DynamicIntent): ParsedCommand;
@@ -0,0 +1,46 @@
1
+ import { getIntentDef } from "../utils/config.js";
2
+ import { loadRules } from "../utils/config.js";
3
+ const CONFIDENCE_THRESHOLD = 0.6;
4
+ export function disambiguate(intent) {
5
+ const def = getIntentDef(intent.intent);
6
+ const missingFields = [];
7
+ const ambiguousFields = [];
8
+ if (def) {
9
+ for (const [fieldName, fieldDef] of Object.entries(def.fields)) {
10
+ if (fieldDef.required && intent.fields[fieldName] === undefined) {
11
+ missingFields.push(fieldName);
12
+ }
13
+ }
14
+ // Check for ambiguous service references
15
+ for (const [fieldName, fieldDef] of Object.entries(def.fields)) {
16
+ if (fieldDef.type === "service" && intent.fields[fieldName]) {
17
+ const candidates = findServiceCandidates(intent.fields[fieldName]);
18
+ if (candidates.length > 1) {
19
+ ambiguousFields.push({ field: fieldName, candidates });
20
+ }
21
+ }
22
+ }
23
+ }
24
+ const needsClarification = missingFields.length > 0 ||
25
+ ambiguousFields.length > 0 ||
26
+ intent.confidence < CONFIDENCE_THRESHOLD;
27
+ return {
28
+ intent,
29
+ missingFields,
30
+ ambiguousFields,
31
+ needsClarification,
32
+ };
33
+ }
34
+ function findServiceCandidates(input) {
35
+ const rules = loadRules();
36
+ const matches = [];
37
+ for (const [canonical, aliases] of Object.entries(rules.serviceAliases)) {
38
+ for (const alias of aliases) {
39
+ if (alias.includes(input) || input.includes(alias)) {
40
+ matches.push(canonical);
41
+ break;
42
+ }
43
+ }
44
+ }
45
+ return matches;
46
+ }
@@ -0,0 +1,14 @@
1
+ import type { DynamicIntent } from "../types/intent.js";
2
+ /**
3
+ * Fuzzy file resolver.
4
+ *
5
+ * When an intent has fuzzyResolve fields, this attempts to find the actual
6
+ * file path on the target server using a combination of:
7
+ * - exact match
8
+ * - find by filename
9
+ * - find by partial name (fuzzy)
10
+ * - locate database
11
+ *
12
+ * Returns the intent with resolved file paths in the fields.
13
+ */
14
+ export declare function resolveFuzzyFields(intent: DynamicIntent): Promise<DynamicIntent>;