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,237 @@
1
+ import { z } from "zod";
2
+ export declare const SynonymEntry: z.ZodObject<{
3
+ intent: z.ZodString;
4
+ phrases: z.ZodArray<z.ZodString, "many">;
5
+ }, "strip", z.ZodTypeAny, {
6
+ intent: string;
7
+ phrases: string[];
8
+ }, {
9
+ intent: string;
10
+ phrases: string[];
11
+ }>;
12
+ export declare const AliasEntry: z.ZodObject<{
13
+ canonical: z.ZodString;
14
+ aliases: z.ZodArray<z.ZodString, "many">;
15
+ }, "strip", z.ZodTypeAny, {
16
+ canonical: string;
17
+ aliases: string[];
18
+ }, {
19
+ canonical: string;
20
+ aliases: string[];
21
+ }>;
22
+ export declare const RulesConfig: z.ZodObject<{
23
+ version: z.ZodString;
24
+ intentSynonyms: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>;
25
+ environmentAliases: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>;
26
+ serviceAliases: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>;
27
+ }, "strip", z.ZodTypeAny, {
28
+ version: string;
29
+ intentSynonyms: Record<string, string[]>;
30
+ environmentAliases: Record<string, string[]>;
31
+ serviceAliases: Record<string, string[]>;
32
+ }, {
33
+ version: string;
34
+ intentSynonyms: Record<string, string[]>;
35
+ environmentAliases: Record<string, string[]>;
36
+ serviceAliases: Record<string, string[]>;
37
+ }>;
38
+ export type RulesConfig = z.infer<typeof RulesConfig>;
39
+ export declare const RulePatchChange: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
40
+ type: z.ZodLiteral<"add_intent_synonym">;
41
+ intent: z.ZodString;
42
+ phrase: z.ZodString;
43
+ }, "strip", z.ZodTypeAny, {
44
+ type: "add_intent_synonym";
45
+ intent: string;
46
+ phrase: string;
47
+ }, {
48
+ type: "add_intent_synonym";
49
+ intent: string;
50
+ phrase: string;
51
+ }>, z.ZodObject<{
52
+ type: z.ZodLiteral<"add_env_alias">;
53
+ canonical: z.ZodString;
54
+ alias: z.ZodString;
55
+ }, "strip", z.ZodTypeAny, {
56
+ type: "add_env_alias";
57
+ canonical: string;
58
+ alias: string;
59
+ }, {
60
+ type: "add_env_alias";
61
+ canonical: string;
62
+ alias: string;
63
+ }>, z.ZodObject<{
64
+ type: z.ZodLiteral<"add_service_alias">;
65
+ canonical: z.ZodString;
66
+ alias: z.ZodString;
67
+ }, "strip", z.ZodTypeAny, {
68
+ type: "add_service_alias";
69
+ canonical: string;
70
+ alias: string;
71
+ }, {
72
+ type: "add_service_alias";
73
+ canonical: string;
74
+ alias: string;
75
+ }>, z.ZodObject<{
76
+ type: z.ZodLiteral<"remove_intent_synonym">;
77
+ intent: z.ZodString;
78
+ phrase: z.ZodString;
79
+ }, "strip", z.ZodTypeAny, {
80
+ type: "remove_intent_synonym";
81
+ intent: string;
82
+ phrase: string;
83
+ }, {
84
+ type: "remove_intent_synonym";
85
+ intent: string;
86
+ phrase: string;
87
+ }>]>;
88
+ export type RulePatchChange = z.infer<typeof RulePatchChange>;
89
+ export declare const RulePatchTestCase: z.ZodObject<{
90
+ input: z.ZodString;
91
+ expectedIntent: z.ZodOptional<z.ZodString>;
92
+ expectedFields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
93
+ shouldReject: z.ZodOptional<z.ZodBoolean>;
94
+ }, "strip", z.ZodTypeAny, {
95
+ input: string;
96
+ expectedIntent?: string | undefined;
97
+ expectedFields?: Record<string, unknown> | undefined;
98
+ shouldReject?: boolean | undefined;
99
+ }, {
100
+ input: string;
101
+ expectedIntent?: string | undefined;
102
+ expectedFields?: Record<string, unknown> | undefined;
103
+ shouldReject?: boolean | undefined;
104
+ }>;
105
+ export type RulePatchTestCase = z.infer<typeof RulePatchTestCase>;
106
+ export declare const RulePatch: z.ZodObject<{
107
+ summary: z.ZodString;
108
+ confidence: z.ZodNumber;
109
+ changes: z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
110
+ type: z.ZodLiteral<"add_intent_synonym">;
111
+ intent: z.ZodString;
112
+ phrase: z.ZodString;
113
+ }, "strip", z.ZodTypeAny, {
114
+ type: "add_intent_synonym";
115
+ intent: string;
116
+ phrase: string;
117
+ }, {
118
+ type: "add_intent_synonym";
119
+ intent: string;
120
+ phrase: string;
121
+ }>, z.ZodObject<{
122
+ type: z.ZodLiteral<"add_env_alias">;
123
+ canonical: z.ZodString;
124
+ alias: z.ZodString;
125
+ }, "strip", z.ZodTypeAny, {
126
+ type: "add_env_alias";
127
+ canonical: string;
128
+ alias: string;
129
+ }, {
130
+ type: "add_env_alias";
131
+ canonical: string;
132
+ alias: string;
133
+ }>, z.ZodObject<{
134
+ type: z.ZodLiteral<"add_service_alias">;
135
+ canonical: z.ZodString;
136
+ alias: z.ZodString;
137
+ }, "strip", z.ZodTypeAny, {
138
+ type: "add_service_alias";
139
+ canonical: string;
140
+ alias: string;
141
+ }, {
142
+ type: "add_service_alias";
143
+ canonical: string;
144
+ alias: string;
145
+ }>, z.ZodObject<{
146
+ type: z.ZodLiteral<"remove_intent_synonym">;
147
+ intent: z.ZodString;
148
+ phrase: z.ZodString;
149
+ }, "strip", z.ZodTypeAny, {
150
+ type: "remove_intent_synonym";
151
+ intent: string;
152
+ phrase: string;
153
+ }, {
154
+ type: "remove_intent_synonym";
155
+ intent: string;
156
+ phrase: string;
157
+ }>]>, "many">;
158
+ tests: z.ZodArray<z.ZodObject<{
159
+ input: z.ZodString;
160
+ expectedIntent: z.ZodOptional<z.ZodString>;
161
+ expectedFields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
162
+ shouldReject: z.ZodOptional<z.ZodBoolean>;
163
+ }, "strip", z.ZodTypeAny, {
164
+ input: string;
165
+ expectedIntent?: string | undefined;
166
+ expectedFields?: Record<string, unknown> | undefined;
167
+ shouldReject?: boolean | undefined;
168
+ }, {
169
+ input: string;
170
+ expectedIntent?: string | undefined;
171
+ expectedFields?: Record<string, unknown> | undefined;
172
+ shouldReject?: boolean | undefined;
173
+ }>, "many">;
174
+ warnings: z.ZodArray<z.ZodString, "many">;
175
+ }, "strip", z.ZodTypeAny, {
176
+ confidence: number;
177
+ summary: string;
178
+ changes: ({
179
+ type: "add_intent_synonym";
180
+ intent: string;
181
+ phrase: string;
182
+ } | {
183
+ type: "add_env_alias";
184
+ canonical: string;
185
+ alias: string;
186
+ } | {
187
+ type: "add_service_alias";
188
+ canonical: string;
189
+ alias: string;
190
+ } | {
191
+ type: "remove_intent_synonym";
192
+ intent: string;
193
+ phrase: string;
194
+ })[];
195
+ tests: {
196
+ input: string;
197
+ expectedIntent?: string | undefined;
198
+ expectedFields?: Record<string, unknown> | undefined;
199
+ shouldReject?: boolean | undefined;
200
+ }[];
201
+ warnings: string[];
202
+ }, {
203
+ confidence: number;
204
+ summary: string;
205
+ changes: ({
206
+ type: "add_intent_synonym";
207
+ intent: string;
208
+ phrase: string;
209
+ } | {
210
+ type: "add_env_alias";
211
+ canonical: string;
212
+ alias: string;
213
+ } | {
214
+ type: "add_service_alias";
215
+ canonical: string;
216
+ alias: string;
217
+ } | {
218
+ type: "remove_intent_synonym";
219
+ intent: string;
220
+ phrase: string;
221
+ })[];
222
+ tests: {
223
+ input: string;
224
+ expectedIntent?: string | undefined;
225
+ expectedFields?: Record<string, unknown> | undefined;
226
+ shouldReject?: boolean | undefined;
227
+ }[];
228
+ warnings: string[];
229
+ }>;
230
+ export type RulePatch = z.infer<typeof RulePatch>;
231
+ export interface FailureLog {
232
+ rawText: string;
233
+ timestamp: string;
234
+ parsedIntent: string | null;
235
+ confidence: number;
236
+ error?: string;
237
+ }
@@ -0,0 +1,50 @@
1
+ import { z } from "zod";
2
+ export const SynonymEntry = z.object({
3
+ intent: z.string(),
4
+ phrases: z.array(z.string()),
5
+ });
6
+ export const AliasEntry = z.object({
7
+ canonical: z.string(),
8
+ aliases: z.array(z.string()),
9
+ });
10
+ export const RulesConfig = z.object({
11
+ version: z.string(),
12
+ intentSynonyms: z.record(z.array(z.string())),
13
+ environmentAliases: z.record(z.array(z.string())),
14
+ serviceAliases: z.record(z.array(z.string())),
15
+ });
16
+ export const RulePatchChange = z.discriminatedUnion("type", [
17
+ z.object({
18
+ type: z.literal("add_intent_synonym"),
19
+ intent: z.string(),
20
+ phrase: z.string(),
21
+ }),
22
+ z.object({
23
+ type: z.literal("add_env_alias"),
24
+ canonical: z.string(),
25
+ alias: z.string(),
26
+ }),
27
+ z.object({
28
+ type: z.literal("add_service_alias"),
29
+ canonical: z.string(),
30
+ alias: z.string(),
31
+ }),
32
+ z.object({
33
+ type: z.literal("remove_intent_synonym"),
34
+ intent: z.string(),
35
+ phrase: z.string(),
36
+ }),
37
+ ]);
38
+ export const RulePatchTestCase = z.object({
39
+ input: z.string(),
40
+ expectedIntent: z.string().optional(),
41
+ expectedFields: z.record(z.unknown()).optional(),
42
+ shouldReject: z.boolean().optional(),
43
+ });
44
+ export const RulePatch = z.object({
45
+ summary: z.string(),
46
+ confidence: z.number(),
47
+ changes: z.array(RulePatchChange),
48
+ tests: z.array(RulePatchTestCase),
49
+ warnings: z.array(z.string()),
50
+ });
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Intelligent output analysis.
3
+ *
4
+ * Parses raw command output and adds human-readable commentary:
5
+ * - Load: checks vCPUs vs load average, flags overload
6
+ * - Disk: flags partitions above thresholds, checks specific paths
7
+ * - Memory: flags low memory, high swap usage
8
+ * - Directory: detects project types, file breakdowns
9
+ */
10
+ export declare function analyzeLoad(output: string): string;
11
+ export interface DiskPartition {
12
+ filesystem: string;
13
+ size: string;
14
+ used: string;
15
+ available: string;
16
+ usePercent: number;
17
+ mountPoint: string;
18
+ }
19
+ export declare function analyzeDisk(output: string, specificPath?: string): string;
20
+ export declare function analyzeMemory(output: string): string;
21
+ /**
22
+ * Analyze command output based on intent and add commentary.
23
+ * Returns empty string if no analysis applicable.
24
+ */
25
+ export declare function analyzeOutput(intent: string, output: string, fields: Record<string, unknown>): string;
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Intelligent output analysis.
3
+ *
4
+ * Parses raw command output and adds human-readable commentary:
5
+ * - Load: checks vCPUs vs load average, flags overload
6
+ * - Disk: flags partitions above thresholds, checks specific paths
7
+ * - Memory: flags low memory, high swap usage
8
+ * - Directory: detects project types, file breakdowns
9
+ */
10
+ import { analyzeDirectory as analyzeDirectoryImpl } from "./dirAnalysis.js";
11
+ function analyzeDirectoryOutput(output) {
12
+ return analyzeDirectoryImpl(output);
13
+ }
14
+ const c = {
15
+ reset: "\x1b[0m",
16
+ bold: "\x1b[1m",
17
+ dim: "\x1b[2m",
18
+ green: "\x1b[32m",
19
+ yellow: "\x1b[33m",
20
+ red: "\x1b[31m",
21
+ cyan: "\x1b[36m",
22
+ };
23
+ // ─── Load Analysis ───────────────────────────────────────────────────────────
24
+ export function analyzeLoad(output) {
25
+ const lines = [];
26
+ // Extract load averages from uptime output
27
+ // "load average: 0.52, 0.58, 0.59"
28
+ const loadMatch = output.match(/load average:\s*([\d.]+),?\s*([\d.]+),?\s*([\d.]+)/);
29
+ if (!loadMatch)
30
+ return "";
31
+ const load1 = parseFloat(loadMatch[1]);
32
+ const load5 = parseFloat(loadMatch[2]);
33
+ const load15 = parseFloat(loadMatch[3]);
34
+ // Extract CPU count from nproc or lscpu output
35
+ let cpuCount = 1;
36
+ const nprocMatch = output.match(/^(\d+)$/m);
37
+ const lscpuMatch = output.match(/CPU\(s\):\s*(\d+)/);
38
+ if (nprocMatch)
39
+ cpuCount = parseInt(nprocMatch[1]);
40
+ else if (lscpuMatch)
41
+ cpuCount = parseInt(lscpuMatch[1]);
42
+ // Also try /proc/cpuinfo count
43
+ const procMatch = output.match(/processor\s*:\s*(\d+)/g);
44
+ if (procMatch && procMatch.length > cpuCount)
45
+ cpuCount = procMatch.length;
46
+ lines.push(`\n${c.bold}${c.cyan}── Analysis ──${c.reset}`);
47
+ lines.push(` vCPUs: ${c.bold}${cpuCount}${c.reset}`);
48
+ lines.push(` Load: ${c.bold}${load1}${c.reset} (1m) ${load5} (5m) ${load15} (15m)`);
49
+ // Load ratio: load / cpuCount
50
+ const ratio1 = load1 / cpuCount;
51
+ const ratio5 = load5 / cpuCount;
52
+ if (ratio1 > 2.0) {
53
+ lines.push(` ${c.red}⚠ CRITICAL: Load is ${ratio1.toFixed(1)}x your CPU capacity!${c.reset}`);
54
+ lines.push(` ${c.red} System is severely overloaded. Processes are queuing.${c.reset}`);
55
+ }
56
+ else if (ratio1 > 1.0) {
57
+ lines.push(` ${c.yellow}⚠ HIGH: Load exceeds CPU count (${ratio1.toFixed(1)}x capacity).${c.reset}`);
58
+ lines.push(` ${c.yellow} Some processes are waiting for CPU time.${c.reset}`);
59
+ }
60
+ else if (ratio1 > 0.7) {
61
+ lines.push(` ${c.yellow}⚠ MODERATE: ${(ratio1 * 100).toFixed(0)}% CPU utilization.${c.reset}`);
62
+ lines.push(` ${c.dim} Approaching capacity — monitor closely.${c.reset}`);
63
+ }
64
+ else {
65
+ lines.push(` ${c.green}✓ OK: ${(ratio1 * 100).toFixed(0)}% CPU utilization. Healthy.${c.reset}`);
66
+ }
67
+ // Trend
68
+ if (load1 > load15 * 1.5) {
69
+ lines.push(` ${c.yellow}↑ Load is trending UP (${load15} → ${load1}).${c.reset}`);
70
+ }
71
+ else if (load1 < load15 * 0.5) {
72
+ lines.push(` ${c.green}↓ Load is trending DOWN (${load15} → ${load1}).${c.reset}`);
73
+ }
74
+ else {
75
+ lines.push(` ${c.dim}→ Load is stable.${c.reset}`);
76
+ }
77
+ return lines.join("\n");
78
+ }
79
+ export function analyzeDisk(output, specificPath) {
80
+ const lines = [];
81
+ const partitions = parseDfOutput(output);
82
+ if (partitions.length === 0)
83
+ return "";
84
+ lines.push(`\n${c.bold}${c.cyan}── Analysis ──${c.reset}`);
85
+ // If asking about a specific path, find the matching partition
86
+ if (specificPath) {
87
+ const match = findPartitionForPath(partitions, specificPath);
88
+ if (match) {
89
+ lines.push(` Path ${c.bold}${specificPath}${c.reset} is on ${c.bold}${match.mountPoint}${c.reset}`);
90
+ lines.push(` ${formatPartitionHealth(match)}`);
91
+ return lines.join("\n");
92
+ }
93
+ else {
94
+ lines.push(` ${c.yellow}Could not find partition for path: ${specificPath}${c.reset}`);
95
+ }
96
+ }
97
+ // Overall analysis
98
+ let criticalCount = 0;
99
+ let warningCount = 0;
100
+ // Filter to real filesystems (skip snap, tmpfs, etc.)
101
+ const realPartitions = partitions.filter((p) => !p.filesystem.startsWith("snap") &&
102
+ !p.filesystem.startsWith("tmpfs") &&
103
+ !p.filesystem.startsWith("none") &&
104
+ !p.filesystem.startsWith("rootfs") &&
105
+ p.mountPoint !== "/snap" &&
106
+ !p.mountPoint.startsWith("/snap/"));
107
+ for (const p of realPartitions) {
108
+ if (p.usePercent >= 95) {
109
+ lines.push(` ${c.red}⚠ CRITICAL: ${p.mountPoint} is ${p.usePercent}% full (${p.available} free)${c.reset}`);
110
+ criticalCount++;
111
+ }
112
+ else if (p.usePercent >= 85) {
113
+ lines.push(` ${c.yellow}⚠ WARNING: ${p.mountPoint} is ${p.usePercent}% full (${p.available} free)${c.reset}`);
114
+ warningCount++;
115
+ }
116
+ else if (p.usePercent >= 70) {
117
+ lines.push(` ${c.dim} ${p.mountPoint}: ${p.usePercent}% used (${p.available} free)${c.reset}`);
118
+ }
119
+ }
120
+ if (criticalCount === 0 && warningCount === 0) {
121
+ lines.push(` ${c.green}✓ All partitions healthy. No space issues.${c.reset}`);
122
+ }
123
+ else {
124
+ if (criticalCount > 0) {
125
+ lines.push(` ${c.red} ${criticalCount} partition(s) critically full!${c.reset}`);
126
+ }
127
+ if (warningCount > 0) {
128
+ lines.push(` ${c.yellow} ${warningCount} partition(s) approaching full.${c.reset}`);
129
+ }
130
+ lines.push(` ${c.dim} Tip: Run "disk analysis" playbook for detailed breakdown.${c.reset}`);
131
+ }
132
+ // Highlight largest partitions
133
+ const sorted = [...realPartitions].sort((a, b) => b.usePercent - a.usePercent);
134
+ if (sorted.length > 0 && !specificPath) {
135
+ lines.push(`\n ${c.bold}Top usage:${c.reset}`);
136
+ for (const p of sorted.slice(0, 5)) {
137
+ const bar = usageBar(p.usePercent);
138
+ lines.push(` ${bar} ${p.usePercent.toString().padStart(3)}% ${p.mountPoint} (${p.used}/${p.size})`);
139
+ }
140
+ }
141
+ return lines.join("\n");
142
+ }
143
+ function parseDfOutput(output) {
144
+ const partitions = [];
145
+ const lines = output.split("\n");
146
+ for (const line of lines) {
147
+ // Match df -h output: Filesystem Size Used Avail Use% Mounted on
148
+ const match = line.match(/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\d+)%\s+(.+)$/);
149
+ if (match) {
150
+ partitions.push({
151
+ filesystem: match[1],
152
+ size: match[2],
153
+ used: match[3],
154
+ available: match[4],
155
+ usePercent: parseInt(match[5]),
156
+ mountPoint: match[6].trim(),
157
+ });
158
+ }
159
+ }
160
+ return partitions;
161
+ }
162
+ function findPartitionForPath(partitions, path) {
163
+ // Find the longest matching mount point
164
+ const lower = path.toLowerCase();
165
+ // Map common names to paths
166
+ const aliases = {
167
+ "documents": ["/home", "/mnt/c/Users", "~/Documents"],
168
+ "downloads": ["/home", "/mnt/c/Users", "~/Downloads"],
169
+ "my documents": ["/home", "/mnt/c/Users"],
170
+ "home": ["/home"],
171
+ "root": ["/"],
172
+ "tmp": ["/tmp"],
173
+ "var": ["/var"],
174
+ "log": ["/var/log", "/var"],
175
+ "www": ["/var/www"],
176
+ "c drive": ["/mnt/c"],
177
+ "d drive": ["/mnt/d"],
178
+ "e drive": ["/mnt/e"],
179
+ "f drive": ["/mnt/f"],
180
+ };
181
+ // Check aliases first
182
+ for (const [alias, paths] of Object.entries(aliases)) {
183
+ if (lower.includes(alias)) {
184
+ for (const p of paths) {
185
+ const match = partitions
186
+ .filter((part) => p.startsWith(part.mountPoint) || part.mountPoint.startsWith(p))
187
+ .sort((a, b) => b.mountPoint.length - a.mountPoint.length)[0];
188
+ if (match)
189
+ return match;
190
+ }
191
+ }
192
+ }
193
+ // Direct path match — find longest mount point that is a prefix
194
+ const sorted = [...partitions].sort((a, b) => b.mountPoint.length - a.mountPoint.length);
195
+ for (const p of sorted) {
196
+ if (path.startsWith(p.mountPoint))
197
+ return p;
198
+ }
199
+ return null;
200
+ }
201
+ function formatPartitionHealth(p) {
202
+ if (p.usePercent >= 95)
203
+ return `${c.red}⚠ CRITICAL: ${p.usePercent}% full! Only ${p.available} free on ${p.size} total.${c.reset}`;
204
+ if (p.usePercent >= 85)
205
+ return `${c.yellow}⚠ WARNING: ${p.usePercent}% full. ${p.available} free on ${p.size} total.${c.reset}`;
206
+ if (p.usePercent >= 70)
207
+ return `${c.dim}Moderate: ${p.usePercent}% full. ${p.available} free on ${p.size} total.${c.reset}`;
208
+ return `${c.green}✓ Healthy: ${p.usePercent}% used. ${p.available} free on ${p.size} total.${c.reset}`;
209
+ }
210
+ function usageBar(percent, width = 20) {
211
+ const filled = Math.round((percent / 100) * width);
212
+ const empty = width - filled;
213
+ const color = percent >= 95 ? c.red : percent >= 85 ? c.yellow : c.green;
214
+ return `${color}${"█".repeat(filled)}${"░".repeat(empty)}${c.reset}`;
215
+ }
216
+ // ─── Memory Analysis ─────────────────────────────────────────────────────────
217
+ export function analyzeMemory(output) {
218
+ const lines = [];
219
+ // Parse "free -h" output
220
+ // Mem: 31Gi 15Gi 9.5Gi 9.8Mi 6.3Gi 15Gi
221
+ const memMatch = output.match(/Mem:\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/);
222
+ const swapMatch = output.match(/Swap:\s+(\S+)\s+(\S+)\s+(\S+)/);
223
+ if (!memMatch)
224
+ return "";
225
+ const total = memMatch[1];
226
+ const used = memMatch[2];
227
+ const free = memMatch[3];
228
+ const available = memMatch[6];
229
+ lines.push(`\n${c.bold}${c.cyan}── Analysis ──${c.reset}`);
230
+ // Calculate percentage from raw values
231
+ const totalGB = parseSize(total);
232
+ const usedGB = parseSize(used);
233
+ const availGB = parseSize(available);
234
+ if (totalGB > 0) {
235
+ const usedPercent = Math.round((usedGB / totalGB) * 100);
236
+ const availPercent = Math.round((availGB / totalGB) * 100);
237
+ lines.push(` RAM: ${c.bold}${used}${c.reset} used of ${total} (${availPercent}% available)`);
238
+ lines.push(` ${usageBar(usedPercent)}`);
239
+ if (usedPercent >= 95) {
240
+ lines.push(` ${c.red}⚠ CRITICAL: Memory nearly exhausted! OOM killer may activate.${c.reset}`);
241
+ }
242
+ else if (usedPercent >= 85) {
243
+ lines.push(` ${c.yellow}⚠ HIGH: Memory pressure. Consider scaling up or killing processes.${c.reset}`);
244
+ }
245
+ else if (usedPercent >= 70) {
246
+ lines.push(` ${c.dim}Moderate memory usage. Normal for most workloads.${c.reset}`);
247
+ }
248
+ else {
249
+ lines.push(` ${c.green}✓ Memory healthy.${c.reset}`);
250
+ }
251
+ }
252
+ // Swap analysis
253
+ if (swapMatch) {
254
+ const swapTotal = swapMatch[1];
255
+ const swapUsed = swapMatch[2];
256
+ const swapTotalGB = parseSize(swapTotal);
257
+ const swapUsedGB = parseSize(swapUsed);
258
+ if (swapTotalGB > 0) {
259
+ const swapPercent = Math.round((swapUsedGB / swapTotalGB) * 100);
260
+ if (swapPercent > 50) {
261
+ lines.push(` ${c.yellow}⚠ Swap: ${swapUsed}/${swapTotal} used (${swapPercent}%). Heavy swapping degrades performance.${c.reset}`);
262
+ }
263
+ else if (swapUsedGB > 0) {
264
+ lines.push(` ${c.dim}Swap: ${swapUsed}/${swapTotal} used. Some swapping is normal.${c.reset}`);
265
+ }
266
+ else {
267
+ lines.push(` ${c.green}✓ No swap usage.${c.reset}`);
268
+ }
269
+ }
270
+ }
271
+ return lines.join("\n");
272
+ }
273
+ function parseSize(str) {
274
+ const match = str.match(/([\d.]+)(\w+)/);
275
+ if (!match)
276
+ return 0;
277
+ const num = parseFloat(match[1]);
278
+ const unit = match[2].toLowerCase();
279
+ if (unit.startsWith("t"))
280
+ return num * 1024;
281
+ if (unit.startsWith("g"))
282
+ return num;
283
+ if (unit.startsWith("m"))
284
+ return num / 1024;
285
+ if (unit.startsWith("k"))
286
+ return num / (1024 * 1024);
287
+ return num;
288
+ }
289
+ // ─── Router ──────────────────────────────────────────────────────────────────
290
+ /**
291
+ * Analyze command output based on intent and add commentary.
292
+ * Returns empty string if no analysis applicable.
293
+ */
294
+ export function analyzeOutput(intent, output, fields) {
295
+ switch (intent) {
296
+ case "server.check_disk":
297
+ return analyzeDisk(output, fields.target);
298
+ case "server.check_memory":
299
+ return analyzeMemory(output);
300
+ case "server.uptime":
301
+ return analyzeLoad(output);
302
+ case "files.list":
303
+ return analyzeDirectoryOutput(output);
304
+ default:
305
+ return "";
306
+ }
307
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Auto-backup system.
3
+ *
4
+ * Before any file modification (copy, move, remove, env.set),
5
+ * creates a timestamped backup in ~/.notoken/backups/.
6
+ * Backups are kept for a configurable number of hours (default: 6).
7
+ *
8
+ * Also supports manual rollback.
9
+ */
10
+ export interface BackupRecord {
11
+ id: string;
12
+ originalPath: string;
13
+ backupPath: string;
14
+ timestamp: string;
15
+ intent: string;
16
+ expiresAt: string;
17
+ }
18
+ /**
19
+ * Create a backup of a file before modifying it.
20
+ * Returns the backup record, or null if the file doesn't exist.
21
+ */
22
+ export declare function createBackup(originalPath: string, intent: string, retentionHours?: number): BackupRecord | null;
23
+ /**
24
+ * Generate the remote backup command to run before modifying a file.
25
+ * This returns a shell command string that creates a backup on the remote server.
26
+ */
27
+ export declare function getRemoteBackupCommand(filePath: string): string;
28
+ /**
29
+ * Rollback a file from backup.
30
+ */
31
+ export declare function rollback(id: string): boolean;
32
+ /**
33
+ * List all current backups.
34
+ */
35
+ export declare function listBackups(): BackupRecord[];
36
+ /**
37
+ * Clean up expired backups.
38
+ */
39
+ export declare function cleanExpiredBackups(): number;
40
+ /**
41
+ * Format backup list for display.
42
+ */
43
+ export declare function formatBackupList(records: BackupRecord[]): string;