@massu/core 2.5.2 → 2.5.4
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/CHANGELOG.md +93 -0
- package/agents/massu-output-scorer.md +2 -2
- package/agents/massu-plan-auditor.md +1 -1
- package/commands/_shared-references/test-first-protocol.md +4 -4
- package/commands/_shared-references/verification-table.md +5 -5
- package/commands/massu-audit-deps.md +2 -2
- package/commands/massu-batch.md +3 -3
- package/commands/massu-checkpoint.md +7 -7
- package/commands/massu-ci-fix.md +1 -1
- package/commands/massu-cleanup.md +16 -16
- package/commands/massu-commit.md +7 -7
- package/commands/massu-create-plan.md +7 -7
- package/commands/massu-dead-code.md +3 -3
- package/commands/massu-debug/references/common-shortcuts.md +1 -1
- package/commands/massu-debug/references/investigation-phases.md +4 -4
- package/commands/massu-debug/references/report-format.md +2 -2
- package/commands/massu-deploy.md +1 -1
- package/commands/massu-deps.md +5 -5
- package/commands/massu-doc-gen.md +3 -3
- package/commands/massu-docs.md +6 -6
- package/commands/massu-estimate.md +3 -3
- package/commands/massu-golden-path/references/competitive-mode.md +2 -2
- package/commands/massu-golden-path/references/phase-1-plan-creation.md +1 -1
- package/commands/massu-golden-path/references/phase-2-implementation.md +2 -2
- package/commands/massu-golden-path/references/phase-4-commit.md +3 -3
- package/commands/massu-golden-path/references/phase-5-push.md +5 -5
- package/commands/massu-guide.md +3 -3
- package/commands/massu-hotfix.md +14 -14
- package/commands/massu-loop/references/checkpoint-audit.md +3 -3
- package/commands/massu-loop/references/iteration-structure.md +3 -3
- package/commands/massu-loop/references/loop-controller.md +5 -5
- package/commands/massu-loop/references/plan-extraction.md +4 -4
- package/commands/massu-loop/references/vr-plan-spec.md +3 -3
- package/commands/massu-loop-playwright.md +5 -5
- package/commands/massu-loop.md +1 -1
- package/commands/massu-new-feature.md +6 -24
- package/commands/massu-perf.md +1 -1
- package/commands/massu-plan.md +21 -21
- package/commands/massu-push-light.md +9 -9
- package/commands/massu-recap.md +1 -1
- package/commands/massu-refactor.md +8 -8
- package/commands/massu-release.md +3 -3
- package/commands/massu-review.md +1 -1
- package/commands/massu-rollback.md +6 -6
- package/commands/massu-scaffold-page.md +1 -1
- package/commands/massu-scaffold-router.md +1 -1
- package/commands/massu-status.md +9 -9
- package/commands/massu-tdd.md +2 -2
- package/commands/massu-test.md +13 -13
- package/commands/massu-verify-playwright.md +1 -1
- package/commands/massu-verify.md +20 -20
- package/dist/cli.js +1265 -876
- package/dist/hooks/auto-learning-pipeline.js +517 -56
- package/dist/hooks/classify-failure.js +495 -47
- package/dist/hooks/cost-tracker.js +65 -27
- package/dist/hooks/fix-detector.js +496 -47
- package/dist/hooks/incident-pipeline.js +72 -28
- package/dist/hooks/intent-suggester.js +65 -27
- package/dist/hooks/memory-recall.js +65 -27
- package/dist/hooks/post-edit-context.js +65 -27
- package/dist/hooks/post-tool-use.js +70 -29
- package/dist/hooks/pre-compact.js +65 -27
- package/dist/hooks/pre-delete-check.js +65 -27
- package/dist/hooks/pre-tool-use-gate.js +193 -37
- package/dist/hooks/quality-event.js +65 -27
- package/dist/hooks/rule-enforcement-pipeline.js +65 -27
- package/dist/hooks/security-gate.js +197 -38
- package/dist/hooks/session-end.js +68 -27
- package/dist/hooks/session-start.js +116 -61
- package/dist/hooks/user-prompt.js +65 -27
- package/docs/SECURITY.md +1 -1
- package/fragments/feature-components.md +15 -0
- package/fragments/feature-components.python.md +15 -0
- package/fragments/feature-components.typescript.md +15 -0
- package/package.json +6 -5
- package/patterns/testing-patterns.md +8 -8
- package/protocols/plan-implementation.md +2 -2
- package/protocols/verification.md +4 -4
- package/reference/lessons-learned.md +4 -4
- package/reference/vr-verification-reference.md +9 -9
- package/templates/aspnet/massu.config.yaml +2 -0
- package/templates/go-chi/massu.config.yaml +2 -0
- package/templates/multi-runtime/massu.config.yaml +4 -2
- package/templates/phoenix/massu.config.yaml +2 -0
- package/templates/python-django/massu.config.yaml +3 -1
- package/templates/python-fastapi/massu.config.yaml +3 -1
- package/templates/python-flask/massu.config.yaml +3 -1
- package/templates/rails/massu.config.yaml +4 -2
- package/templates/rust-actix/massu.config.yaml +2 -0
- package/templates/spring/massu.config.yaml +2 -0
- package/templates/swift-ios/massu.config.yaml +3 -1
- package/templates/ts-nestjs/massu.config.yaml +2 -0
- package/templates/ts-nextjs/massu.config.yaml +2 -0
package/dist/cli.js
CHANGED
|
@@ -743,6 +743,42 @@ var init_memory_path = __esm({
|
|
|
743
743
|
}
|
|
744
744
|
});
|
|
745
745
|
|
|
746
|
+
// src/lib/corrections-md.ts
|
|
747
|
+
function formatCorrectionEntry(e2) {
|
|
748
|
+
const one = (s) => s.replace(/\s*\n+\s*/g, " ").trim();
|
|
749
|
+
const lines = [`### ${e2.date} - ${one(e2.title)}`];
|
|
750
|
+
if (e2.wrong) lines.push(`- **Wrong**: ${one(e2.wrong)}`);
|
|
751
|
+
if (e2.correction) lines.push(`- **Correction**: ${one(e2.correction)}`);
|
|
752
|
+
lines.push(`- **Rule**: ${one(e2.rule)}`);
|
|
753
|
+
for (const [k3, v3] of Object.entries(e2.extra ?? {})) {
|
|
754
|
+
lines.push(`- ${k3}: ${one(String(v3))}`);
|
|
755
|
+
}
|
|
756
|
+
return `
|
|
757
|
+
${lines.join("\n")}
|
|
758
|
+
`;
|
|
759
|
+
}
|
|
760
|
+
function correctionsPath(memoryDir) {
|
|
761
|
+
return memoryDir.endsWith("/") ? `${memoryDir}${CORRECTIONS_FILENAME}` : `${memoryDir}/${CORRECTIONS_FILENAME}`;
|
|
762
|
+
}
|
|
763
|
+
function insertCorrectionEntry(existing, entry) {
|
|
764
|
+
const rendered = formatCorrectionEntry(entry);
|
|
765
|
+
const base = existing.length > 0 ? existing : CORRECTIONS_HEADER;
|
|
766
|
+
const archiveIdx = base.indexOf(ARCHIVE_MARKER);
|
|
767
|
+
if (archiveIdx > 0) {
|
|
768
|
+
return base.slice(0, archiveIdx) + rendered + base.slice(archiveIdx);
|
|
769
|
+
}
|
|
770
|
+
return base + rendered;
|
|
771
|
+
}
|
|
772
|
+
var CORRECTIONS_FILENAME, CORRECTIONS_HEADER, ARCHIVE_MARKER;
|
|
773
|
+
var init_corrections_md = __esm({
|
|
774
|
+
"src/lib/corrections-md.ts"() {
|
|
775
|
+
"use strict";
|
|
776
|
+
CORRECTIONS_FILENAME = "corrections.md";
|
|
777
|
+
CORRECTIONS_HEADER = "# Corrections\n";
|
|
778
|
+
ARCHIVE_MARKER = "## Archived";
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
|
|
746
782
|
// src/config-memory-schema.ts
|
|
747
783
|
import { z } from "zod";
|
|
748
784
|
var MemoryConfigSchema;
|
|
@@ -776,7 +812,7 @@ var init_config_memory_schema = __esm({
|
|
|
776
812
|
message: "memory.recall.embedEndpoint must be an http(s) URL"
|
|
777
813
|
}).optional(),
|
|
778
814
|
embedModel: z.string().min(1).optional()
|
|
779
|
-
}).
|
|
815
|
+
}).prefault({}),
|
|
780
816
|
// --- Contradiction / supersede gate (plan-living-memory-slice-2-temporal-model, P5-001) ---
|
|
781
817
|
// When a new high-value memory (decision/correction) is written, find
|
|
782
818
|
// semantically-related existing records and, if the new one contradicts an
|
|
@@ -805,7 +841,7 @@ var init_config_memory_schema = __esm({
|
|
|
805
841
|
annotateSuperseded: z.boolean().default(false),
|
|
806
842
|
// Time budget (ms) for the contradiction check; exceeded → fail-open (ADD).
|
|
807
843
|
budgetMs: z.number().int().positive().default(800)
|
|
808
|
-
}).
|
|
844
|
+
}).prefault({}),
|
|
809
845
|
// --- Background consolidation, the "sleep-time" pass
|
|
810
846
|
// (plan-living-memory-slice-3-consolidation) ---
|
|
811
847
|
// Keeps memory sharp over years: dedupes, distills dying sessions into
|
|
@@ -851,7 +887,7 @@ var init_config_memory_schema = __esm({
|
|
|
851
887
|
// Surface optional upgrades (e.g. "a local model was detected") in chat.
|
|
852
888
|
suggestUpgrades: z.boolean().default(true),
|
|
853
889
|
suggestIntervalDays: z.number().int().positive().default(30)
|
|
854
|
-
}).
|
|
890
|
+
}).prefault({}),
|
|
855
891
|
// A-20 (Slice 4) — memory FILES: the mirror, and the one switch that decides whether
|
|
856
892
|
// Massu may ever WRITE into the user's memory directory.
|
|
857
893
|
//
|
|
@@ -876,7 +912,7 @@ var init_config_memory_schema = __esm({
|
|
|
876
912
|
// turn of EVERY session, so an unbounded index is a permanent context tax — and the
|
|
877
913
|
// per-session cap bounds only the RATE, never the total.
|
|
878
914
|
indexMaxLines: z.number().int().min(1).default(50)
|
|
879
|
-
}).
|
|
915
|
+
}).prefault({}),
|
|
880
916
|
// --- Cross-repo memory surfacing (plan-living-memory-slice-5-cross-repo-surfacing) ---
|
|
881
917
|
// A decision made in one of your repos can surface in another — opt-in per
|
|
882
918
|
// decision AND opt-in per repo, signed, verified, and materialized ONLY on
|
|
@@ -901,8 +937,8 @@ var init_config_memory_schema = __esm({
|
|
|
901
937
|
enabled: z.boolean().default(true),
|
|
902
938
|
maxCrossRepoItems: z.number().int().min(0).default(1),
|
|
903
939
|
minScore: z.number().optional()
|
|
904
|
-
}).
|
|
905
|
-
}).
|
|
940
|
+
}).prefault({})
|
|
941
|
+
}).prefault({})
|
|
906
942
|
}).optional();
|
|
907
943
|
}
|
|
908
944
|
});
|
|
@@ -911,7 +947,11 @@ var init_config_memory_schema = __esm({
|
|
|
911
947
|
var config_exports = {};
|
|
912
948
|
__export(config_exports, {
|
|
913
949
|
AdapterLocalPathSchema: () => AdapterLocalPathSchema,
|
|
950
|
+
AutoLearningConfigSchema: () => AutoLearningConfigSchema,
|
|
951
|
+
DEFAULT_KNOWLEDGE_SOURCE_FILES: () => DEFAULT_KNOWLEDGE_SOURCE_FILES,
|
|
914
952
|
LSPConfigSchema: () => LSPConfigSchema,
|
|
953
|
+
RawConfigSchema: () => RawConfigSchema,
|
|
954
|
+
VerificationEntrySchema: () => VerificationEntrySchema,
|
|
915
955
|
getConfig: () => getConfig,
|
|
916
956
|
getProjectRoot: () => getProjectRoot,
|
|
917
957
|
getResolvedApiKeySource: () => getResolvedApiKeySource,
|
|
@@ -1101,13 +1141,19 @@ function resetConfig() {
|
|
|
1101
1141
|
_projectRoot = null;
|
|
1102
1142
|
_resolvedApiKeySource = "none";
|
|
1103
1143
|
}
|
|
1104
|
-
var DomainConfigSchema, PatternRuleConfigSchema, CostModelSchema, AnalyticsConfigSchema, CustomPatternSchema, GovernanceConfigSchema, SecurityPatternSchema, SecurityConfigSchema, TeamConfigSchema, RegressionConfigSchema, AutoLearningConfigSchema, CloudConfigSchema, ConventionsConfigSchema, PythonDomainConfigSchema, PythonConfigSchema, PathsConfigSchema, LanguageFrameworkEntrySchema, FrameworkConfigSchema, DetectedConfigSchema, VerificationEntrySchema, VerificationConfigSchema, CanonicalPathsSchema, VerificationTypesSchema, DetectionRuleEntrySchema, DetectionConfigSchema, WatchConfigSchema, AdapterLocalPathSchema, AdaptersConfigSchema, TelemetryConfigSchema, LSPConfigSchema, RawConfigSchema, _config, _resolvedApiKeySource, _projectRoot;
|
|
1144
|
+
var DEFAULT_KNOWLEDGE_SOURCE_FILES, DomainConfigSchema, PatternRuleConfigSchema, CostModelSchema, AnalyticsConfigSchema, CustomPatternSchema, GovernanceConfigSchema, SecurityPatternSchema, SecurityConfigSchema, TeamConfigSchema, RegressionConfigSchema, AutoLearningConfigSchema, CloudConfigSchema, ConventionsConfigSchema, PythonDomainConfigSchema, PythonConfigSchema, PathsConfigSchema, LanguageFrameworkEntrySchema, FrameworkConfigSchema, DetectedConfigSchema, VerificationEntrySchema, VerificationConfigSchema, CanonicalPathsSchema, VerificationTypesSchema, DetectionRuleEntrySchema, DetectionConfigSchema, WatchConfigSchema, AdapterLocalPathSchema, AdaptersConfigSchema, TelemetryConfigSchema, LSPConfigSchema, RawConfigSchema, _config, _resolvedApiKeySource, _projectRoot;
|
|
1105
1145
|
var init_config = __esm({
|
|
1106
1146
|
"src/config.ts"() {
|
|
1107
1147
|
"use strict";
|
|
1108
1148
|
init_memory_path();
|
|
1149
|
+
init_corrections_md();
|
|
1109
1150
|
init_credentials();
|
|
1110
1151
|
init_config_memory_schema();
|
|
1152
|
+
DEFAULT_KNOWLEDGE_SOURCE_FILES = [
|
|
1153
|
+
"CLAUDE.md",
|
|
1154
|
+
"MEMORY.md",
|
|
1155
|
+
CORRECTIONS_FILENAME
|
|
1156
|
+
];
|
|
1111
1157
|
DomainConfigSchema = z2.object({
|
|
1112
1158
|
name: z2.string().default("Unknown"),
|
|
1113
1159
|
routers: z2.array(z2.string()).default([]),
|
|
@@ -1236,34 +1282,40 @@ var init_config = __esm({
|
|
|
1236
1282
|
fixDetection: z2.object({
|
|
1237
1283
|
enabled: z2.boolean().default(true),
|
|
1238
1284
|
lookbackDays: z2.number().default(7),
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1285
|
+
// ⛔ NO DEFAULT LIST HERE, DELIBERATELY — the heuristics are the SSOT.
|
|
1286
|
+
//
|
|
1287
|
+
// This carried a hand-typed copy of all eight signal names while
|
|
1288
|
+
// `fix-detector.ts` already read `config…signals ?? FIX_HEURISTICS.map(h => h.name)`.
|
|
1289
|
+
// Because zod ALWAYS materialised the default, that fallback — the correct expression
|
|
1290
|
+
// — was unreachable, and the duplicate was the only thing in effect. Two authoring
|
|
1291
|
+
// sites for one vocabulary, and the drift is SILENT: a name listed here that no longer
|
|
1292
|
+
// exists simply never matches `enabledSignals.has(...)`, so a retired heuristic lingers
|
|
1293
|
+
// as a dead entry with no error. Retiring `method_name_correction` would have landed at
|
|
1294
|
+
// one site of two (CR-74).
|
|
1295
|
+
//
|
|
1296
|
+
// Optional, so an ABSENT value reaches the `??` and the heuristic list defines itself
|
|
1297
|
+
// (Rule 25). An explicit list in massu.config.yaml still overrides, which is the only
|
|
1298
|
+
// behaviour anyone actually configured — measured: `fix-detector.ts:267` is the sole
|
|
1299
|
+
// consumer and no config file in the repo sets it.
|
|
1300
|
+
signals: z2.array(z2.string()).optional()
|
|
1301
|
+
}).prefault({}),
|
|
1250
1302
|
failureClassification: z2.object({
|
|
1251
1303
|
enabled: z2.boolean().default(true),
|
|
1252
1304
|
thresholds: z2.object({
|
|
1253
1305
|
known: z2.number().default(5),
|
|
1254
1306
|
similar: z2.number().default(3)
|
|
1255
|
-
}).
|
|
1307
|
+
}).prefault({}),
|
|
1256
1308
|
scoring: z2.object({
|
|
1257
1309
|
diffPatternWeight: z2.number().default(3),
|
|
1258
1310
|
filePatternWeight: z2.number().default(2),
|
|
1259
1311
|
promptKeywordWeight: z2.number().default(2)
|
|
1260
|
-
}).
|
|
1261
|
-
}).
|
|
1312
|
+
}).prefault({})
|
|
1313
|
+
}).prefault({}),
|
|
1262
1314
|
pipeline: z2.object({
|
|
1263
1315
|
requireIncidentReport: z2.boolean().default(true),
|
|
1264
1316
|
requirePreventionRule: z2.boolean().default(true),
|
|
1265
1317
|
requireEnforcement: z2.boolean().default(true)
|
|
1266
|
-
}).
|
|
1318
|
+
}).prefault({}),
|
|
1267
1319
|
// plan-v0.2-interactive-rule-approval P-D-008 / P-D-009: project-configured
|
|
1268
1320
|
// custom destinations for the rule-candidate funnel. The classifier matches
|
|
1269
1321
|
// a candidate to one of these entries when none of the framework
|
|
@@ -1296,7 +1348,7 @@ var init_config = __esm({
|
|
|
1296
1348
|
memory: z2.boolean().default(true),
|
|
1297
1349
|
analytics: z2.boolean().default(true),
|
|
1298
1350
|
audit: z2.boolean().default(true)
|
|
1299
|
-
}).
|
|
1351
|
+
}).prefault({ memory: true, analytics: true, audit: true })
|
|
1300
1352
|
}).optional();
|
|
1301
1353
|
ConventionsConfigSchema = z2.object({
|
|
1302
1354
|
claudeDirName: z2.string().default(".claude").refine(
|
|
@@ -1327,7 +1379,7 @@ var init_config = __esm({
|
|
|
1327
1379
|
"session-state",
|
|
1328
1380
|
"agents"
|
|
1329
1381
|
]),
|
|
1330
|
-
knowledgeSourceFiles: z2.array(z2.string()).default([
|
|
1382
|
+
knowledgeSourceFiles: z2.array(z2.string()).default([...DEFAULT_KNOWLEDGE_SOURCE_FILES]),
|
|
1331
1383
|
excludePatterns: z2.array(z2.string()).default(["/ARCHIVE/", "/SESSION-HISTORY/"])
|
|
1332
1384
|
}).optional();
|
|
1333
1385
|
PythonDomainConfigSchema = z2.object({
|
|
@@ -1343,6 +1395,23 @@ var init_config = __esm({
|
|
|
1343
1395
|
}).optional();
|
|
1344
1396
|
PathsConfigSchema = z2.object({
|
|
1345
1397
|
source: z2.string().default("src"),
|
|
1398
|
+
/**
|
|
1399
|
+
* Where this stack's tests live, relative to the project root.
|
|
1400
|
+
*
|
|
1401
|
+
* Added 2026-09-20 for P4.3 of docs/plans/2026-09-20-stack-conformant-asset-rendering.md.
|
|
1402
|
+
* ~25 shipped assets told every customer their tests belong in
|
|
1403
|
+
* `packages/core/src/__tests__/` — massu's OWN layout — and no vocabulary could express
|
|
1404
|
+
* the alternative because none existed. A test directory is a first-class property of
|
|
1405
|
+
* every stack (`tests/` on FastAPI, `test/` on Rails, `src/test/java` on Spring,
|
|
1406
|
+
* `Tests/` on SwiftPM, and Go keeps `_test.go` beside the source), so it belongs here
|
|
1407
|
+
* rather than in prose.
|
|
1408
|
+
*
|
|
1409
|
+
* OPTIONAL, so every config written before this key existed keeps loading unchanged; an
|
|
1410
|
+
* asset that needs it uses `{{paths.tests | default("...")}}`. All 13 shipped templates
|
|
1411
|
+
* declare it, each with the convention it follows recorded at the site (G32 option 2),
|
|
1412
|
+
* and `stack-template-conformance.test.ts` asserts that.
|
|
1413
|
+
*/
|
|
1414
|
+
tests: z2.string().optional(),
|
|
1346
1415
|
aliases: z2.record(z2.string(), z2.string()).default({ "@": "src" }),
|
|
1347
1416
|
monorepo_roots: z2.array(z2.string()).optional(),
|
|
1348
1417
|
routers: z2.string().optional(),
|
|
@@ -1452,14 +1521,14 @@ var init_config = __esm({
|
|
|
1452
1521
|
project: z2.object({
|
|
1453
1522
|
name: z2.string().default("my-project"),
|
|
1454
1523
|
root: z2.string().default("auto")
|
|
1455
|
-
}).
|
|
1456
|
-
framework: FrameworkConfigSchema.
|
|
1524
|
+
}).prefault({ name: "my-project", root: "auto" }),
|
|
1525
|
+
framework: FrameworkConfigSchema.prefault({
|
|
1457
1526
|
type: "typescript",
|
|
1458
1527
|
router: "none",
|
|
1459
1528
|
orm: "none",
|
|
1460
1529
|
ui: "none"
|
|
1461
1530
|
}),
|
|
1462
|
-
paths: PathsConfigSchema.
|
|
1531
|
+
paths: PathsConfigSchema.prefault({ source: "src", aliases: { "@": "src" } }),
|
|
1463
1532
|
toolPrefix: z2.string().default("massu"),
|
|
1464
1533
|
dbAccessPattern: z2.string().optional(),
|
|
1465
1534
|
knownMismatches: z2.record(z2.string(), z2.record(z2.string(), z2.string())).optional(),
|
|
@@ -4508,8 +4577,188 @@ var init_memory_file_ingest = __esm({
|
|
|
4508
4577
|
}
|
|
4509
4578
|
});
|
|
4510
4579
|
|
|
4580
|
+
// src/shipped-template-schema.ts
|
|
4581
|
+
import { z as z3 } from "zod";
|
|
4582
|
+
function validateShippedTemplate(raw, stack) {
|
|
4583
|
+
const result = ShippedTemplateConfigSchema.safeParse(raw);
|
|
4584
|
+
if (result.success) return { ok: true, stack, issues: [] };
|
|
4585
|
+
return {
|
|
4586
|
+
ok: false,
|
|
4587
|
+
stack,
|
|
4588
|
+
issues: result.error.issues.map((issue) => {
|
|
4589
|
+
const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
4590
|
+
return `${stack}: ${path}: ${issue.message}`;
|
|
4591
|
+
})
|
|
4592
|
+
};
|
|
4593
|
+
}
|
|
4594
|
+
var REQUIRED_TEMPLATE_VERIFY_KEYS, ShippedTemplateConfigSchema;
|
|
4595
|
+
var init_shipped_template_schema = __esm({
|
|
4596
|
+
"src/shipped-template-schema.ts"() {
|
|
4597
|
+
"use strict";
|
|
4598
|
+
init_config();
|
|
4599
|
+
REQUIRED_TEMPLATE_VERIFY_KEYS = ["test", "type", "lint"];
|
|
4600
|
+
ShippedTemplateConfigSchema = RawConfigSchema.extend({
|
|
4601
|
+
// Optional for a customer; MANDATORY for anything massu ships.
|
|
4602
|
+
verification: z3.record(z3.string(), VerificationEntrySchema)
|
|
4603
|
+
}).superRefine((cfg, ctx) => {
|
|
4604
|
+
const framework = cfg.framework;
|
|
4605
|
+
const primary = framework?.primary ?? framework?.type;
|
|
4606
|
+
if (typeof primary !== "string" || primary.length === 0) {
|
|
4607
|
+
ctx.addIssue({
|
|
4608
|
+
code: z3.ZodIssueCode.custom,
|
|
4609
|
+
path: ["framework", "primary"],
|
|
4610
|
+
message: "a shipped template must resolve a primary language (framework.primary or framework.type)"
|
|
4611
|
+
});
|
|
4612
|
+
return;
|
|
4613
|
+
}
|
|
4614
|
+
const primaryEntry = cfg.verification?.[primary];
|
|
4615
|
+
if (primaryEntry === void 0) {
|
|
4616
|
+
ctx.addIssue({
|
|
4617
|
+
code: z3.ZodIssueCode.custom,
|
|
4618
|
+
path: ["verification", primary],
|
|
4619
|
+
message: `a shipped template must declare verification commands for its primary language '${primary}'`
|
|
4620
|
+
});
|
|
4621
|
+
return;
|
|
4622
|
+
}
|
|
4623
|
+
for (const required of REQUIRED_TEMPLATE_VERIFY_KEYS) {
|
|
4624
|
+
const value = primaryEntry[required];
|
|
4625
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
4626
|
+
ctx.addIssue({
|
|
4627
|
+
code: z3.ZodIssueCode.custom,
|
|
4628
|
+
path: ["verification", primary, required],
|
|
4629
|
+
message: `a shipped template must declare a non-empty '${required}' command for '${primary}'`
|
|
4630
|
+
});
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4633
|
+
});
|
|
4634
|
+
}
|
|
4635
|
+
});
|
|
4636
|
+
|
|
4637
|
+
// src/commands/asset-resolution.ts
|
|
4638
|
+
import { existsSync as existsSync11, readdirSync as readdirSync4, statSync as statSync3 } from "fs";
|
|
4639
|
+
import { resolve as resolve5, dirname as dirname8 } from "path";
|
|
4640
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4641
|
+
function dirHoldsAssets(dir) {
|
|
4642
|
+
if (!existsSync11(dir)) return false;
|
|
4643
|
+
try {
|
|
4644
|
+
return readdirSync4(dir).some(
|
|
4645
|
+
(e2) => e2.endsWith(".md") || statSync3(resolve5(dir, e2)).isDirectory()
|
|
4646
|
+
);
|
|
4647
|
+
} catch {
|
|
4648
|
+
return false;
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
function resolveAssetDir(assetName) {
|
|
4652
|
+
const cwd = process.cwd();
|
|
4653
|
+
const candidates = [
|
|
4654
|
+
// 1. npm-installed: node_modules/@massu/core/{assetName}
|
|
4655
|
+
resolve5(cwd, "node_modules/@massu/core", assetName),
|
|
4656
|
+
// 2. Relative to compiled dist/cli.js → ../{assetName}
|
|
4657
|
+
resolve5(__dirname, "..", assetName),
|
|
4658
|
+
// 3. Relative to source src/commands/ → ../../{assetName}
|
|
4659
|
+
resolve5(__dirname, "../..", assetName)
|
|
4660
|
+
];
|
|
4661
|
+
for (const c2 of candidates) {
|
|
4662
|
+
if (dirHoldsAssets(c2)) return c2;
|
|
4663
|
+
}
|
|
4664
|
+
return null;
|
|
4665
|
+
}
|
|
4666
|
+
function pickVariant(baseName, sourceDir, framework) {
|
|
4667
|
+
const candidates = [];
|
|
4668
|
+
const seenLangs = /* @__PURE__ */ new Set();
|
|
4669
|
+
function pushCandidate(lang, sub) {
|
|
4670
|
+
if (seenLangs.has(lang)) return;
|
|
4671
|
+
seenLangs.add(lang);
|
|
4672
|
+
candidates.push({ lang, subFramework: sub && sub.length > 0 ? sub : void 0 });
|
|
4673
|
+
}
|
|
4674
|
+
const primary = framework.primary ?? framework.type;
|
|
4675
|
+
if (primary && primary !== "multi") {
|
|
4676
|
+
let primarySub;
|
|
4677
|
+
if (framework.languages && framework.languages[primary]?.framework) {
|
|
4678
|
+
primarySub = framework.languages[primary].framework;
|
|
4679
|
+
} else {
|
|
4680
|
+
const passthrough2 = framework;
|
|
4681
|
+
const block = passthrough2[primary];
|
|
4682
|
+
if (block && typeof block === "object") {
|
|
4683
|
+
const fw = block.framework;
|
|
4684
|
+
if (typeof fw === "string" && fw.length > 0) primarySub = fw;
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
pushCandidate(primary, primarySub);
|
|
4688
|
+
}
|
|
4689
|
+
if (framework.languages) {
|
|
4690
|
+
for (const lang of Object.keys(framework.languages)) {
|
|
4691
|
+
const entry = framework.languages[lang];
|
|
4692
|
+
if (entry && typeof entry.framework === "string" && entry.framework.length > 0) {
|
|
4693
|
+
pushCandidate(lang, entry.framework);
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
const passthrough = framework;
|
|
4698
|
+
for (const lang of PASSTHROUGH_LANG_KEYS) {
|
|
4699
|
+
if (seenLangs.has(lang)) continue;
|
|
4700
|
+
const block = passthrough[lang];
|
|
4701
|
+
if (block && typeof block === "object") {
|
|
4702
|
+
const fw = block.framework;
|
|
4703
|
+
if (typeof fw === "string" && fw.length > 0) {
|
|
4704
|
+
pushCandidate(lang, fw);
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
}
|
|
4708
|
+
for (const cand of candidates) {
|
|
4709
|
+
if (cand.subFramework) {
|
|
4710
|
+
const subPath = resolve5(sourceDir, `${baseName}.${cand.lang}-${cand.subFramework}.md`);
|
|
4711
|
+
if (existsSync11(subPath)) {
|
|
4712
|
+
return { kind: "hit", suffix: `.${cand.lang}-${cand.subFramework}` };
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
const langPath = resolve5(sourceDir, `${baseName}.${cand.lang}.md`);
|
|
4716
|
+
if (existsSync11(langPath)) {
|
|
4717
|
+
return { kind: "hit", suffix: `.${cand.lang}` };
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
const defaultPath = resolve5(sourceDir, `${baseName}.md`);
|
|
4721
|
+
if (existsSync11(defaultPath)) {
|
|
4722
|
+
return { kind: "hit", suffix: "" };
|
|
4723
|
+
}
|
|
4724
|
+
if (framework.type === "multi" && !framework.primary) {
|
|
4725
|
+
process.stderr.write(
|
|
4726
|
+
"massu: warning - framework.type=multi but framework.primary is undefined; falling back to default templates\n"
|
|
4727
|
+
);
|
|
4728
|
+
return { kind: "fallback", reason: "multi-without-primary" };
|
|
4729
|
+
}
|
|
4730
|
+
return { kind: "miss" };
|
|
4731
|
+
}
|
|
4732
|
+
function isVariantFilename(entry) {
|
|
4733
|
+
return /^[^.]+\.[^.]+\.md$/.test(entry);
|
|
4734
|
+
}
|
|
4735
|
+
function variantFilename(baseName, choice) {
|
|
4736
|
+
if (choice.kind === "miss") return null;
|
|
4737
|
+
const suffix = choice.kind === "hit" ? choice.suffix : "";
|
|
4738
|
+
return suffix === "" ? `${baseName}.md` : `${baseName}${suffix}.md`;
|
|
4739
|
+
}
|
|
4740
|
+
var __filename, __dirname, PASSTHROUGH_LANG_KEYS;
|
|
4741
|
+
var init_asset_resolution = __esm({
|
|
4742
|
+
"src/commands/asset-resolution.ts"() {
|
|
4743
|
+
"use strict";
|
|
4744
|
+
__filename = fileURLToPath2(import.meta.url);
|
|
4745
|
+
__dirname = dirname8(__filename);
|
|
4746
|
+
PASSTHROUGH_LANG_KEYS = [
|
|
4747
|
+
"typescript",
|
|
4748
|
+
"javascript",
|
|
4749
|
+
"python",
|
|
4750
|
+
"swift",
|
|
4751
|
+
"rust",
|
|
4752
|
+
"go"
|
|
4753
|
+
];
|
|
4754
|
+
}
|
|
4755
|
+
});
|
|
4756
|
+
|
|
4511
4757
|
// src/commands/template-engine.ts
|
|
4512
|
-
function
|
|
4758
|
+
function fragmentDiagnostic(name) {
|
|
4759
|
+
return `<!-- massu: no fragment "${name}" for this stack, and no default -->`;
|
|
4760
|
+
}
|
|
4761
|
+
function renderTemplate(template, vars, opts = {}) {
|
|
4513
4762
|
const out = [];
|
|
4514
4763
|
const len = template.length;
|
|
4515
4764
|
let i = 0;
|
|
@@ -4527,6 +4776,18 @@ function renderTemplate(template, vars) {
|
|
|
4527
4776
|
throw new TemplateParseError("unclosed `{{` (no matching `}}`)", tokenStart);
|
|
4528
4777
|
}
|
|
4529
4778
|
const inner = template.slice(i + 2, closeIdx);
|
|
4779
|
+
const fragmentName = matchFragmentToken(inner);
|
|
4780
|
+
if (fragmentName !== null) {
|
|
4781
|
+
const body = opts.resolveFragment ? opts.resolveFragment(fragmentName) : null;
|
|
4782
|
+
if (body === null) {
|
|
4783
|
+
out.push(fragmentDiagnostic(fragmentName));
|
|
4784
|
+
} else {
|
|
4785
|
+
if (matchFragmentAnywhere(body)) throw new FragmentNestingError(fragmentName);
|
|
4786
|
+
out.push(renderTemplate(body, vars, {}));
|
|
4787
|
+
}
|
|
4788
|
+
i = closeIdx + 2;
|
|
4789
|
+
continue;
|
|
4790
|
+
}
|
|
4530
4791
|
if (isJsxPassThrough(inner)) {
|
|
4531
4792
|
out.push("{{");
|
|
4532
4793
|
out.push(inner);
|
|
@@ -4663,7 +4924,14 @@ function stringify(value) {
|
|
|
4663
4924
|
if (typeof value === "string") return value;
|
|
4664
4925
|
return String(value);
|
|
4665
4926
|
}
|
|
4666
|
-
|
|
4927
|
+
function matchFragmentToken(inner) {
|
|
4928
|
+
const m3 = /^>\s*([A-Za-z0-9][A-Za-z0-9_-]*)\s*$/.exec(inner);
|
|
4929
|
+
return m3 ? m3[1] : null;
|
|
4930
|
+
}
|
|
4931
|
+
function matchFragmentAnywhere(body) {
|
|
4932
|
+
return /\{\{\s*>\s*[A-Za-z0-9]/.test(body);
|
|
4933
|
+
}
|
|
4934
|
+
var MissingVariableError, TemplateParseError, FragmentNestingError;
|
|
4667
4935
|
var init_template_engine = __esm({
|
|
4668
4936
|
"src/commands/template-engine.ts"() {
|
|
4669
4937
|
"use strict";
|
|
@@ -4683,22 +4951,264 @@ var init_template_engine = __esm({
|
|
|
4683
4951
|
this.position = position;
|
|
4684
4952
|
}
|
|
4685
4953
|
};
|
|
4954
|
+
FragmentNestingError = class extends Error {
|
|
4955
|
+
constructor(fragmentName) {
|
|
4956
|
+
super(
|
|
4957
|
+
`fragment "${fragmentName}" contains a nested {{> \u2026 }}. Fragments may not include other fragments: one level keeps resolution total and removes cycles by construction.`
|
|
4958
|
+
);
|
|
4959
|
+
this.fragmentName = fragmentName;
|
|
4960
|
+
this.name = "FragmentNestingError";
|
|
4961
|
+
}
|
|
4962
|
+
fragmentName;
|
|
4963
|
+
};
|
|
4964
|
+
}
|
|
4965
|
+
});
|
|
4966
|
+
|
|
4967
|
+
// src/commands/fragments.ts
|
|
4968
|
+
import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
|
|
4969
|
+
import { resolve as resolve6 } from "path";
|
|
4970
|
+
function makeFragmentResolver(framework, stats) {
|
|
4971
|
+
const fragmentsDir = resolveAssetDir("fragments");
|
|
4972
|
+
return (name) => {
|
|
4973
|
+
if (!fragmentsDir) {
|
|
4974
|
+
stats.missing++;
|
|
4975
|
+
return null;
|
|
4976
|
+
}
|
|
4977
|
+
const choice = pickVariant(name, fragmentsDir, framework);
|
|
4978
|
+
if (choice.kind !== "hit") {
|
|
4979
|
+
stats.missing++;
|
|
4980
|
+
return null;
|
|
4981
|
+
}
|
|
4982
|
+
const file = resolve6(fragmentsDir, `${name}${choice.suffix}.md`);
|
|
4983
|
+
if (!existsSync12(file)) {
|
|
4984
|
+
stats.missing++;
|
|
4985
|
+
return null;
|
|
4986
|
+
}
|
|
4987
|
+
if (choice.suffix === "") stats.defaulted++;
|
|
4988
|
+
else stats.resolved++;
|
|
4989
|
+
return readFileSync7(file, "utf-8");
|
|
4990
|
+
};
|
|
4991
|
+
}
|
|
4992
|
+
var init_fragments = __esm({
|
|
4993
|
+
"src/commands/fragments.ts"() {
|
|
4994
|
+
"use strict";
|
|
4995
|
+
init_asset_resolution();
|
|
4996
|
+
}
|
|
4997
|
+
});
|
|
4998
|
+
|
|
4999
|
+
// src/stack-capabilities.ts
|
|
5000
|
+
function stackLabel(config) {
|
|
5001
|
+
const name = config.project?.name;
|
|
5002
|
+
if (typeof name === "string" && name.length > 0 && name !== "{{PROJECT_NAME}}") return name;
|
|
5003
|
+
return config.framework?.primary ?? config.framework?.type ?? "<unnamed stack>";
|
|
5004
|
+
}
|
|
5005
|
+
function isVerifyKey(candidate) {
|
|
5006
|
+
return ALL_VERIFY_KEYS.includes(candidate);
|
|
5007
|
+
}
|
|
5008
|
+
function resolveStackCapabilities(config) {
|
|
5009
|
+
const label = stackLabel(config);
|
|
5010
|
+
const primaryLanguage = config.framework?.primary ?? config.framework?.type;
|
|
5011
|
+
if (typeof primaryLanguage !== "string" || primaryLanguage.length === 0) {
|
|
5012
|
+
throw new StackContractError(
|
|
5013
|
+
label,
|
|
5014
|
+
`${label}: cannot resolve a primary language \u2014 neither framework.primary nor framework.type is set.`
|
|
5015
|
+
);
|
|
5016
|
+
}
|
|
5017
|
+
const verification = config.verification;
|
|
5018
|
+
if (verification === void 0 || verification === null) {
|
|
5019
|
+
throw new StackContractError(
|
|
5020
|
+
label,
|
|
5021
|
+
`${label}: no top-level 'verification:' block. Every stack must declare ${REQUIRED_VERIFY_KEYS.join("/")} for its primary language (${primaryLanguage}).`
|
|
5022
|
+
);
|
|
5023
|
+
}
|
|
5024
|
+
const roster = /* @__PURE__ */ new Set([primaryLanguage]);
|
|
5025
|
+
for (const lang of Object.keys(config.framework?.languages ?? {})) roster.add(lang);
|
|
5026
|
+
for (const lang of Object.keys(verification)) roster.add(lang);
|
|
5027
|
+
const verifyByLang = {};
|
|
5028
|
+
for (const lang of roster) {
|
|
5029
|
+
const entry = verification[lang];
|
|
5030
|
+
if (!entry) continue;
|
|
5031
|
+
const resolved = {};
|
|
5032
|
+
for (const [candidate, value] of Object.entries(entry)) {
|
|
5033
|
+
if (!isVerifyKey(candidate)) continue;
|
|
5034
|
+
if (typeof value === "string" && value.length > 0) resolved[candidate] = value;
|
|
5035
|
+
}
|
|
5036
|
+
if (Object.keys(resolved).length > 0) verifyByLang[lang] = resolved;
|
|
5037
|
+
}
|
|
5038
|
+
const primaryEntry = verifyByLang[primaryLanguage];
|
|
5039
|
+
if (primaryEntry === void 0) {
|
|
5040
|
+
throw new StackContractError(
|
|
5041
|
+
label,
|
|
5042
|
+
`${label}: 'verification:' declares no commands for the primary language '${primaryLanguage}'. Declared languages: ${Object.keys(verifyByLang).join(", ") || "(none)"}.`
|
|
5043
|
+
);
|
|
5044
|
+
}
|
|
5045
|
+
for (const required of REQUIRED_VERIFY_KEYS) {
|
|
5046
|
+
if (primaryEntry[required] === void 0) {
|
|
5047
|
+
throw new StackContractError(
|
|
5048
|
+
label,
|
|
5049
|
+
`${label}: required verification key '${required}' is not declared for the primary language '${primaryLanguage}'. Required: ${REQUIRED_VERIFY_KEYS.join(", ")}.`,
|
|
5050
|
+
required
|
|
5051
|
+
);
|
|
5052
|
+
}
|
|
5053
|
+
}
|
|
5054
|
+
const verify = {};
|
|
5055
|
+
for (const declaredKey of ALL_VERIFY_KEYS) verify[declaredKey] = primaryEntry[declaredKey];
|
|
5056
|
+
const declared = new Set(
|
|
5057
|
+
ALL_VERIFY_KEYS.filter((candidate) => primaryEntry[candidate] !== void 0)
|
|
5058
|
+
);
|
|
5059
|
+
const languages = [
|
|
5060
|
+
primaryLanguage,
|
|
5061
|
+
...[...roster].filter((lang) => lang !== primaryLanguage).sort()
|
|
5062
|
+
];
|
|
5063
|
+
return {
|
|
5064
|
+
contractVersion: STACK_CONTRACT_VERSION,
|
|
5065
|
+
primaryLanguage,
|
|
5066
|
+
languages,
|
|
5067
|
+
verify,
|
|
5068
|
+
verifyByLang,
|
|
5069
|
+
paths: config.paths,
|
|
5070
|
+
declared
|
|
5071
|
+
};
|
|
5072
|
+
}
|
|
5073
|
+
var STACK_CONTRACT_VERSION, REQUIRED_VERIFY_KEYS, OPTIONAL_VERIFY_KEYS, ALL_VERIFY_KEYS, StackContractError;
|
|
5074
|
+
var init_stack_capabilities = __esm({
|
|
5075
|
+
"src/stack-capabilities.ts"() {
|
|
5076
|
+
"use strict";
|
|
5077
|
+
STACK_CONTRACT_VERSION = 1;
|
|
5078
|
+
REQUIRED_VERIFY_KEYS = ["test", "type", "lint"];
|
|
5079
|
+
OPTIONAL_VERIFY_KEYS = ["build", "syntax"];
|
|
5080
|
+
ALL_VERIFY_KEYS = [
|
|
5081
|
+
...REQUIRED_VERIFY_KEYS,
|
|
5082
|
+
...OPTIONAL_VERIFY_KEYS
|
|
5083
|
+
];
|
|
5084
|
+
StackContractError = class extends Error {
|
|
5085
|
+
stackName;
|
|
5086
|
+
verifyKey;
|
|
5087
|
+
constructor(stackName, message, verifyKey) {
|
|
5088
|
+
super(message);
|
|
5089
|
+
this.name = "StackContractError";
|
|
5090
|
+
this.stackName = stackName;
|
|
5091
|
+
this.verifyKey = verifyKey;
|
|
5092
|
+
}
|
|
5093
|
+
};
|
|
5094
|
+
}
|
|
5095
|
+
});
|
|
5096
|
+
|
|
5097
|
+
// src/commands/asset-render.ts
|
|
5098
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
5099
|
+
import { resolve as resolve7 } from "path";
|
|
5100
|
+
function buildTemplateVars() {
|
|
5101
|
+
const config = getConfig();
|
|
5102
|
+
let stack;
|
|
5103
|
+
let verifyVars;
|
|
5104
|
+
try {
|
|
5105
|
+
const caps = resolveStackCapabilities(config);
|
|
5106
|
+
stack = caps;
|
|
5107
|
+
verifyVars = { verify: caps.verify, verifyByLang: caps.verifyByLang };
|
|
5108
|
+
} catch (err) {
|
|
5109
|
+
if (!(err instanceof StackContractError)) throw err;
|
|
5110
|
+
stack = {
|
|
5111
|
+
contractVersion: STACK_CONTRACT_VERSION,
|
|
5112
|
+
satisfied: false,
|
|
5113
|
+
reason: err.message,
|
|
5114
|
+
stackName: err.stackName,
|
|
5115
|
+
unsatisfiedKey: err.verifyKey
|
|
5116
|
+
};
|
|
5117
|
+
process.stderr.write(
|
|
5118
|
+
`massu: stack capability contract UNSATISFIED \u2014 ${err.message}
|
|
5119
|
+
massu: assets that need a verification command will be reported by \`massu doctor\`; add a \`verification:\` block to massu.config.yaml to resolve them.
|
|
5120
|
+
`
|
|
5121
|
+
);
|
|
5122
|
+
verifyVars = {};
|
|
5123
|
+
}
|
|
5124
|
+
return {
|
|
5125
|
+
framework: config.framework,
|
|
5126
|
+
paths: config.paths,
|
|
5127
|
+
detected: config.detected ?? {},
|
|
5128
|
+
config,
|
|
5129
|
+
/**
|
|
5130
|
+
* Present only when the contract is satisfied. Absent is deliberate: an asset that
|
|
5131
|
+
* needs a command it cannot get must fail ITS OWN file loudly rather than render an
|
|
5132
|
+
* empty string, which is a check that runs nothing and exits 0.
|
|
5133
|
+
*/
|
|
5134
|
+
...verifyVars,
|
|
5135
|
+
/** Always present, satisfied or not, so a consumer can ask WHICH state it is in. */
|
|
5136
|
+
stack,
|
|
5137
|
+
// P-H006 (plan-stage-c-high-batch): RESERVED CLAUDE CODE PLACEHOLDER.
|
|
5138
|
+
// Claude Code reads `{{ARGUMENTS}}` as a runtime placeholder inside
|
|
5139
|
+
// slash-command files. The Massu template engine has no native concept
|
|
5140
|
+
// of reserved literals; we model the placeholder as a variable whose
|
|
5141
|
+
// value IS the literal `{{ARGUMENTS}}` string. Because the engine never
|
|
5142
|
+
// re-renders output, this passes through verbatim. Closes the bug class
|
|
5143
|
+
// where `/massu-article-review`, `/massu-autoresearch`, etc. silently
|
|
5144
|
+
// failed to install because the engine threw MissingVariableError on
|
|
5145
|
+
// their {{ARGUMENTS}} usage.
|
|
5146
|
+
ARGUMENTS: "{{ARGUMENTS}}"
|
|
5147
|
+
};
|
|
5148
|
+
}
|
|
5149
|
+
function makeRenderContext(framework) {
|
|
5150
|
+
const fragments = { resolved: 0, defaulted: 0, missing: 0 };
|
|
5151
|
+
return {
|
|
5152
|
+
vars: buildTemplateVars(),
|
|
5153
|
+
resolveFragment: makeFragmentResolver(framework, fragments),
|
|
5154
|
+
fragments
|
|
5155
|
+
};
|
|
5156
|
+
}
|
|
5157
|
+
function renderResolvedAsset(baseName, sourceDir, framework, ctx) {
|
|
5158
|
+
const choice = pickVariant(baseName, sourceDir, framework);
|
|
5159
|
+
const sourceFilename = variantFilename(baseName, choice);
|
|
5160
|
+
if (sourceFilename === null) return { kind: "miss" };
|
|
5161
|
+
return renderAssetFile(resolve7(sourceDir, sourceFilename), sourceFilename, ctx);
|
|
5162
|
+
}
|
|
5163
|
+
function renderAssetFile(sourcePath, sourceFilename, ctx) {
|
|
5164
|
+
let rawContent;
|
|
5165
|
+
try {
|
|
5166
|
+
rawContent = readFileSync8(sourcePath, "utf-8");
|
|
5167
|
+
} catch (err) {
|
|
5168
|
+
return {
|
|
5169
|
+
kind: "error",
|
|
5170
|
+
sourcePath,
|
|
5171
|
+
message: err instanceof Error ? err.message : String(err)
|
|
5172
|
+
};
|
|
5173
|
+
}
|
|
5174
|
+
try {
|
|
5175
|
+
return {
|
|
5176
|
+
kind: "ok",
|
|
5177
|
+
sourcePath,
|
|
5178
|
+
sourceFilename,
|
|
5179
|
+
content: renderTemplate(rawContent, ctx.vars, { resolveFragment: ctx.resolveFragment })
|
|
5180
|
+
};
|
|
5181
|
+
} catch (err) {
|
|
5182
|
+
if (err instanceof MissingVariableError || err instanceof TemplateParseError) {
|
|
5183
|
+
return { kind: "error", sourcePath, message: err.message };
|
|
5184
|
+
}
|
|
5185
|
+
throw err;
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
var init_asset_render = __esm({
|
|
5189
|
+
"src/commands/asset-render.ts"() {
|
|
5190
|
+
"use strict";
|
|
5191
|
+
init_config();
|
|
5192
|
+
init_template_engine();
|
|
5193
|
+
init_fragments();
|
|
5194
|
+
init_asset_resolution();
|
|
5195
|
+
init_stack_capabilities();
|
|
4686
5196
|
}
|
|
4687
5197
|
});
|
|
4688
5198
|
|
|
4689
5199
|
// src/lib/settings-local.ts
|
|
4690
5200
|
import {
|
|
4691
5201
|
closeSync as closeSync2,
|
|
4692
|
-
existsSync as
|
|
5202
|
+
existsSync as existsSync13,
|
|
4693
5203
|
fsyncSync as fsyncSync2,
|
|
4694
5204
|
mkdirSync as mkdirSync7,
|
|
4695
5205
|
openSync as openSync2,
|
|
4696
|
-
readFileSync as
|
|
5206
|
+
readFileSync as readFileSync9,
|
|
4697
5207
|
renameSync,
|
|
4698
5208
|
rmSync as rmSync5,
|
|
4699
5209
|
writeSync
|
|
4700
5210
|
} from "fs";
|
|
4701
|
-
import { dirname as
|
|
5211
|
+
import { dirname as dirname9, resolve as resolve8 } from "path";
|
|
4702
5212
|
function atomicWriteFile(targetPath, content, mode = 420) {
|
|
4703
5213
|
const tmpPath = `${targetPath}.${process.pid}.tmp`;
|
|
4704
5214
|
try {
|
|
@@ -4712,7 +5222,7 @@ function atomicWriteFile(targetPath, content, mode = 420) {
|
|
|
4712
5222
|
}
|
|
4713
5223
|
renameSync(tmpPath, targetPath);
|
|
4714
5224
|
} catch (err) {
|
|
4715
|
-
if (
|
|
5225
|
+
if (existsSync13(tmpPath)) {
|
|
4716
5226
|
try {
|
|
4717
5227
|
rmSync5(tmpPath, { force: true });
|
|
4718
5228
|
} catch {
|
|
@@ -4722,11 +5232,11 @@ function atomicWriteFile(targetPath, content, mode = 420) {
|
|
|
4722
5232
|
}
|
|
4723
5233
|
}
|
|
4724
5234
|
function readSettingsAtPath(absolutePath) {
|
|
4725
|
-
if (!
|
|
5235
|
+
if (!existsSync13(absolutePath)) {
|
|
4726
5236
|
return {};
|
|
4727
5237
|
}
|
|
4728
5238
|
try {
|
|
4729
|
-
const raw =
|
|
5239
|
+
const raw = readFileSync9(absolutePath, "utf-8");
|
|
4730
5240
|
const parsed = JSON.parse(raw);
|
|
4731
5241
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4732
5242
|
return {};
|
|
@@ -4737,12 +5247,12 @@ function readSettingsAtPath(absolutePath) {
|
|
|
4737
5247
|
}
|
|
4738
5248
|
}
|
|
4739
5249
|
function readSettingsLocal(claudeDir) {
|
|
4740
|
-
return readSettingsAtPath(
|
|
5250
|
+
return readSettingsAtPath(resolve8(claudeDir, "settings.local.json"));
|
|
4741
5251
|
}
|
|
4742
5252
|
function writeSettingsLocalAtomic(claudeDir, settings) {
|
|
4743
|
-
const targetPath =
|
|
4744
|
-
const dir =
|
|
4745
|
-
if (!
|
|
5253
|
+
const targetPath = resolve8(claudeDir, "settings.local.json");
|
|
5254
|
+
const dir = dirname9(targetPath);
|
|
5255
|
+
if (!existsSync13(dir)) {
|
|
4746
5256
|
mkdirSync7(dir, { recursive: true });
|
|
4747
5257
|
}
|
|
4748
5258
|
const content = JSON.stringify(settings, null, 2) + "\n";
|
|
@@ -4967,7 +5477,6 @@ var init_permissions = __esm({
|
|
|
4967
5477
|
var install_commands_exports = {};
|
|
4968
5478
|
__export(install_commands_exports, {
|
|
4969
5479
|
MASSU_OWNED_PATHS: () => MASSU_OWNED_PATHS,
|
|
4970
|
-
buildTemplateVars: () => buildTemplateVars,
|
|
4971
5480
|
canonicalizeOwned: () => canonicalizeOwned,
|
|
4972
5481
|
hashContent: () => hashContent,
|
|
4973
5482
|
installAll: () => installAll,
|
|
@@ -4975,52 +5484,20 @@ __export(install_commands_exports, {
|
|
|
4975
5484
|
isMassuOwned: () => isMassuOwned,
|
|
4976
5485
|
loadManifest: () => loadManifest,
|
|
4977
5486
|
massuOwnedSourceIsValid: () => massuOwnedSourceIsValid,
|
|
4978
|
-
pickVariant: () => pickVariant,
|
|
4979
|
-
resolveAssetDir: () => resolveAssetDir,
|
|
4980
|
-
resolveCommandsDir: () => resolveCommandsDir,
|
|
4981
5487
|
runInstallCommands: () => runInstallCommands,
|
|
4982
5488
|
runWithManifest: () => runWithManifest,
|
|
4983
5489
|
saveManifest: () => saveManifest,
|
|
4984
5490
|
syncDirectory: () => syncDirectory
|
|
4985
5491
|
});
|
|
4986
5492
|
import {
|
|
4987
|
-
existsSync as
|
|
4988
|
-
readFileSync as
|
|
5493
|
+
existsSync as existsSync14,
|
|
5494
|
+
readFileSync as readFileSync10,
|
|
4989
5495
|
mkdirSync as mkdirSync8,
|
|
4990
|
-
readdirSync as
|
|
4991
|
-
statSync as
|
|
5496
|
+
readdirSync as readdirSync5,
|
|
5497
|
+
statSync as statSync4
|
|
4992
5498
|
} from "fs";
|
|
4993
|
-
import { resolve as
|
|
4994
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5499
|
+
import { resolve as resolve9, join as join8 } from "path";
|
|
4995
5500
|
import { createHash as createHash3 } from "crypto";
|
|
4996
|
-
function dirHoldsAssets(dir) {
|
|
4997
|
-
if (!existsSync12(dir)) return false;
|
|
4998
|
-
try {
|
|
4999
|
-
return readdirSync4(dir).some(
|
|
5000
|
-
(e2) => e2.endsWith(".md") || statSync3(resolve6(dir, e2)).isDirectory()
|
|
5001
|
-
);
|
|
5002
|
-
} catch {
|
|
5003
|
-
return false;
|
|
5004
|
-
}
|
|
5005
|
-
}
|
|
5006
|
-
function resolveAssetDir(assetName) {
|
|
5007
|
-
const cwd = process.cwd();
|
|
5008
|
-
const candidates = [
|
|
5009
|
-
// 1. npm-installed: node_modules/@massu/core/{assetName}
|
|
5010
|
-
resolve6(cwd, "node_modules/@massu/core", assetName),
|
|
5011
|
-
// 2. Relative to compiled dist/cli.js → ../{assetName}
|
|
5012
|
-
resolve6(__dirname, "..", assetName),
|
|
5013
|
-
// 3. Relative to source src/commands/ → ../../{assetName}
|
|
5014
|
-
resolve6(__dirname, "../..", assetName)
|
|
5015
|
-
];
|
|
5016
|
-
for (const c2 of candidates) {
|
|
5017
|
-
if (dirHoldsAssets(c2)) return c2;
|
|
5018
|
-
}
|
|
5019
|
-
return null;
|
|
5020
|
-
}
|
|
5021
|
-
function resolveCommandsDir() {
|
|
5022
|
-
return resolveAssetDir("commands");
|
|
5023
|
-
}
|
|
5024
5501
|
function isMassuOwned(manifestKey) {
|
|
5025
5502
|
return MASSU_OWNED_PATHS.includes(manifestKey);
|
|
5026
5503
|
}
|
|
@@ -5029,10 +5506,10 @@ function canonicalizeOwned(content) {
|
|
|
5029
5506
|
}
|
|
5030
5507
|
function massuOwnedSourceIsValid(resolvedSourcePath, content) {
|
|
5031
5508
|
const sidecar = `${resolvedSourcePath}.sha256`;
|
|
5032
|
-
if (!
|
|
5509
|
+
if (!existsSync14(sidecar)) return false;
|
|
5033
5510
|
let expected;
|
|
5034
5511
|
try {
|
|
5035
|
-
expected =
|
|
5512
|
+
expected = readFileSync10(sidecar, "utf-8").trim();
|
|
5036
5513
|
} catch {
|
|
5037
5514
|
return false;
|
|
5038
5515
|
}
|
|
@@ -5042,12 +5519,12 @@ function hashContent(content) {
|
|
|
5042
5519
|
return createHash3("sha256").update(content, "utf-8").digest("hex");
|
|
5043
5520
|
}
|
|
5044
5521
|
function loadManifest(claudeDir) {
|
|
5045
|
-
const path =
|
|
5046
|
-
if (!
|
|
5522
|
+
const path = resolve9(claudeDir, MANIFEST_RELPATH);
|
|
5523
|
+
if (!existsSync14(path)) {
|
|
5047
5524
|
return emptyManifest();
|
|
5048
5525
|
}
|
|
5049
5526
|
try {
|
|
5050
|
-
const raw =
|
|
5527
|
+
const raw = readFileSync10(path, "utf-8");
|
|
5051
5528
|
const parsed = JSON.parse(raw);
|
|
5052
5529
|
if (!parsed || typeof parsed !== "object" || !parsed.entries) {
|
|
5053
5530
|
return emptyManifest();
|
|
@@ -5058,11 +5535,11 @@ function loadManifest(claudeDir) {
|
|
|
5058
5535
|
}
|
|
5059
5536
|
}
|
|
5060
5537
|
function saveManifest(claudeDir, manifest) {
|
|
5061
|
-
const dir =
|
|
5062
|
-
if (!
|
|
5538
|
+
const dir = resolve9(claudeDir, ".massu");
|
|
5539
|
+
if (!existsSync14(dir)) {
|
|
5063
5540
|
mkdirSync8(dir, { recursive: true });
|
|
5064
5541
|
}
|
|
5065
|
-
const finalPath =
|
|
5542
|
+
const finalPath = resolve9(dir, "install-manifest.json");
|
|
5066
5543
|
manifest.generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5067
5544
|
atomicWriteFile(finalPath, JSON.stringify(manifest, null, 2));
|
|
5068
5545
|
}
|
|
@@ -5080,86 +5557,17 @@ function runWithManifest(claudeDir, fn) {
|
|
|
5080
5557
|
saveManifest(claudeDir, manifest);
|
|
5081
5558
|
return result;
|
|
5082
5559
|
}
|
|
5083
|
-
function
|
|
5084
|
-
const candidates = [];
|
|
5085
|
-
const seenLangs = /* @__PURE__ */ new Set();
|
|
5086
|
-
function pushCandidate(lang, sub) {
|
|
5087
|
-
if (seenLangs.has(lang)) return;
|
|
5088
|
-
seenLangs.add(lang);
|
|
5089
|
-
candidates.push({ lang, subFramework: sub && sub.length > 0 ? sub : void 0 });
|
|
5090
|
-
}
|
|
5091
|
-
const primary = framework.primary ?? framework.type;
|
|
5092
|
-
if (primary && primary !== "multi") {
|
|
5093
|
-
let primarySub;
|
|
5094
|
-
if (framework.languages && framework.languages[primary]?.framework) {
|
|
5095
|
-
primarySub = framework.languages[primary].framework;
|
|
5096
|
-
} else {
|
|
5097
|
-
const passthrough2 = framework;
|
|
5098
|
-
const block = passthrough2[primary];
|
|
5099
|
-
if (block && typeof block === "object") {
|
|
5100
|
-
const fw = block.framework;
|
|
5101
|
-
if (typeof fw === "string" && fw.length > 0) primarySub = fw;
|
|
5102
|
-
}
|
|
5103
|
-
}
|
|
5104
|
-
pushCandidate(primary, primarySub);
|
|
5105
|
-
}
|
|
5106
|
-
if (framework.languages) {
|
|
5107
|
-
for (const lang of Object.keys(framework.languages)) {
|
|
5108
|
-
const entry = framework.languages[lang];
|
|
5109
|
-
if (entry && typeof entry.framework === "string" && entry.framework.length > 0) {
|
|
5110
|
-
pushCandidate(lang, entry.framework);
|
|
5111
|
-
}
|
|
5112
|
-
}
|
|
5113
|
-
}
|
|
5114
|
-
const passthrough = framework;
|
|
5115
|
-
for (const lang of PASSTHROUGH_LANG_KEYS) {
|
|
5116
|
-
if (seenLangs.has(lang)) continue;
|
|
5117
|
-
const block = passthrough[lang];
|
|
5118
|
-
if (block && typeof block === "object") {
|
|
5119
|
-
const fw = block.framework;
|
|
5120
|
-
if (typeof fw === "string" && fw.length > 0) {
|
|
5121
|
-
pushCandidate(lang, fw);
|
|
5122
|
-
}
|
|
5123
|
-
}
|
|
5124
|
-
}
|
|
5125
|
-
for (const cand of candidates) {
|
|
5126
|
-
if (cand.subFramework) {
|
|
5127
|
-
const subPath = resolve6(sourceDir, `${baseName}.${cand.lang}-${cand.subFramework}.md`);
|
|
5128
|
-
if (existsSync12(subPath)) {
|
|
5129
|
-
return { kind: "hit", suffix: `.${cand.lang}-${cand.subFramework}` };
|
|
5130
|
-
}
|
|
5131
|
-
}
|
|
5132
|
-
const langPath = resolve6(sourceDir, `${baseName}.${cand.lang}.md`);
|
|
5133
|
-
if (existsSync12(langPath)) {
|
|
5134
|
-
return { kind: "hit", suffix: `.${cand.lang}` };
|
|
5135
|
-
}
|
|
5136
|
-
}
|
|
5137
|
-
const defaultPath = resolve6(sourceDir, `${baseName}.md`);
|
|
5138
|
-
if (existsSync12(defaultPath)) {
|
|
5139
|
-
return { kind: "hit", suffix: "" };
|
|
5140
|
-
}
|
|
5141
|
-
if (framework.type === "multi" && !framework.primary) {
|
|
5142
|
-
process.stderr.write(
|
|
5143
|
-
"massu: warning - framework.type=multi but framework.primary is undefined; falling back to default templates\n"
|
|
5144
|
-
);
|
|
5145
|
-
return { kind: "fallback", reason: "multi-without-primary" };
|
|
5146
|
-
}
|
|
5147
|
-
return { kind: "miss" };
|
|
5148
|
-
}
|
|
5149
|
-
function isVariantFilename(entry) {
|
|
5150
|
-
return /^[^.]+\.[^.]+\.md$/.test(entry);
|
|
5151
|
-
}
|
|
5152
|
-
function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPrefix, topLevel = true, templateVars = {}) {
|
|
5560
|
+
function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPrefix, topLevel = true, templateVars = {}, resolveFragment) {
|
|
5153
5561
|
const stats = { installed: 0, updated: 0, skipped: 0, kept: 0 };
|
|
5154
|
-
if (!
|
|
5562
|
+
if (!existsSync14(targetDir)) {
|
|
5155
5563
|
mkdirSync8(targetDir, { recursive: true });
|
|
5156
5564
|
}
|
|
5157
|
-
const entries =
|
|
5565
|
+
const entries = readdirSync5(sourceDir);
|
|
5158
5566
|
for (const entry of entries) {
|
|
5159
|
-
const sourcePath =
|
|
5160
|
-
const entryStat =
|
|
5567
|
+
const sourcePath = resolve9(sourceDir, entry);
|
|
5568
|
+
const entryStat = statSync4(sourcePath);
|
|
5161
5569
|
if (entryStat.isDirectory()) {
|
|
5162
|
-
const subTargetDir =
|
|
5570
|
+
const subTargetDir = resolve9(targetDir, entry);
|
|
5163
5571
|
const subPrefix = manifestKeyPrefix === "" ? entry : `${manifestKeyPrefix}/${entry}`;
|
|
5164
5572
|
const subStats = syncDirectory(
|
|
5165
5573
|
sourcePath,
|
|
@@ -5168,7 +5576,8 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
|
|
|
5168
5576
|
manifest,
|
|
5169
5577
|
subPrefix,
|
|
5170
5578
|
false,
|
|
5171
|
-
templateVars
|
|
5579
|
+
templateVars,
|
|
5580
|
+
resolveFragment
|
|
5172
5581
|
);
|
|
5173
5582
|
stats.installed += subStats.installed;
|
|
5174
5583
|
stats.updated += subStats.updated;
|
|
@@ -5177,46 +5586,39 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
|
|
|
5177
5586
|
continue;
|
|
5178
5587
|
}
|
|
5179
5588
|
if (!entry.endsWith(".md")) continue;
|
|
5589
|
+
if (entry === "README.md") continue;
|
|
5180
5590
|
let sourceFilename = entry;
|
|
5181
5591
|
let baseName = entry.slice(0, -".md".length);
|
|
5182
5592
|
if (topLevel) {
|
|
5183
5593
|
if (isVariantFilename(entry)) continue;
|
|
5184
|
-
const
|
|
5185
|
-
if (
|
|
5186
|
-
|
|
5187
|
-
}
|
|
5188
|
-
const suffix = choice.kind === "hit" ? choice.suffix : "";
|
|
5189
|
-
sourceFilename = suffix === "" ? `${baseName}.md` : `${baseName}${suffix}.md`;
|
|
5190
|
-
}
|
|
5191
|
-
const resolvedSourcePath = resolve6(sourceDir, sourceFilename);
|
|
5192
|
-
if (!existsSync12(resolvedSourcePath)) {
|
|
5193
|
-
continue;
|
|
5594
|
+
const selected = variantFilename(baseName, pickVariant(baseName, sourceDir, framework));
|
|
5595
|
+
if (selected === null) continue;
|
|
5596
|
+
sourceFilename = selected;
|
|
5194
5597
|
}
|
|
5598
|
+
const resolvedSourcePath = resolve9(sourceDir, sourceFilename);
|
|
5195
5599
|
const targetFilename = topLevel ? `${baseName}.md` : entry;
|
|
5196
|
-
const targetPath =
|
|
5197
|
-
const
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
if (
|
|
5203
|
-
process.stderr.write(
|
|
5204
|
-
|
|
5205
|
-
`
|
|
5206
|
-
);
|
|
5600
|
+
const targetPath = resolve9(targetDir, targetFilename);
|
|
5601
|
+
const rendered = renderAssetFile(resolvedSourcePath, sourceFilename, {
|
|
5602
|
+
vars: templateVars,
|
|
5603
|
+
resolveFragment
|
|
5604
|
+
});
|
|
5605
|
+
if (rendered.kind !== "ok") {
|
|
5606
|
+
if (rendered.kind === "error") {
|
|
5607
|
+
process.stderr.write(`massu: skipping ${rendered.sourcePath}: ${rendered.message}
|
|
5608
|
+
`);
|
|
5207
5609
|
stats.skipped++;
|
|
5208
|
-
continue;
|
|
5209
5610
|
}
|
|
5210
|
-
|
|
5611
|
+
continue;
|
|
5211
5612
|
}
|
|
5613
|
+
const sourceContent = rendered.content;
|
|
5212
5614
|
const sourceHash = hashContent(sourceContent);
|
|
5213
5615
|
const manifestKey = manifestKeyPrefix === "" ? targetFilename : `${manifestKeyPrefix}/${targetFilename}`;
|
|
5214
5616
|
const lastInstalledHash = manifest.entries[manifestKey];
|
|
5215
|
-
if (
|
|
5617
|
+
if (existsSync14(targetPath)) {
|
|
5216
5618
|
let existingContent;
|
|
5217
5619
|
try {
|
|
5218
|
-
if (!
|
|
5219
|
-
existingContent =
|
|
5620
|
+
if (!statSync4(targetPath).isFile()) throw new Error("not a regular file");
|
|
5621
|
+
existingContent = readFileSync10(targetPath, "utf-8");
|
|
5220
5622
|
} catch (err) {
|
|
5221
5623
|
process.stderr.write(
|
|
5222
5624
|
`massu: skipping ${manifestKey}: target is not a readable file (${err instanceof Error ? err.message : String(err)}). Remove it and re-install.
|
|
@@ -5276,30 +5678,11 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
|
|
|
5276
5678
|
}
|
|
5277
5679
|
return stats;
|
|
5278
5680
|
}
|
|
5279
|
-
function buildTemplateVars() {
|
|
5280
|
-
const config = getConfig();
|
|
5281
|
-
return {
|
|
5282
|
-
framework: config.framework,
|
|
5283
|
-
paths: config.paths,
|
|
5284
|
-
detected: config.detected ?? {},
|
|
5285
|
-
config,
|
|
5286
|
-
// P-H006 (plan-stage-c-high-batch): RESERVED CLAUDE CODE PLACEHOLDER.
|
|
5287
|
-
// Claude Code reads `{{ARGUMENTS}}` as a runtime placeholder inside
|
|
5288
|
-
// slash-command files. The Massu template engine has no native concept
|
|
5289
|
-
// of reserved literals; we model the placeholder as a variable whose
|
|
5290
|
-
// value IS the literal `{{ARGUMENTS}}` string. Because the engine never
|
|
5291
|
-
// re-renders output, this passes through verbatim. Closes the bug class
|
|
5292
|
-
// where `/massu-article-review`, `/massu-autoresearch`, etc. silently
|
|
5293
|
-
// failed to install because the engine threw MissingVariableError on
|
|
5294
|
-
// their {{ARGUMENTS}} usage.
|
|
5295
|
-
ARGUMENTS: "{{ARGUMENTS}}"
|
|
5296
|
-
};
|
|
5297
|
-
}
|
|
5298
5681
|
function installCommands(projectRoot, opts = {}) {
|
|
5299
5682
|
const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
|
|
5300
|
-
const claudeDir =
|
|
5301
|
-
const targetDir =
|
|
5302
|
-
if (!
|
|
5683
|
+
const claudeDir = resolve9(projectRoot, claudeDirName);
|
|
5684
|
+
const targetDir = resolve9(claudeDir, "commands");
|
|
5685
|
+
if (!existsSync14(targetDir)) {
|
|
5303
5686
|
mkdirSync8(targetDir, { recursive: true });
|
|
5304
5687
|
}
|
|
5305
5688
|
const sourceDir = resolveAssetDir("commands");
|
|
@@ -5309,7 +5692,8 @@ function installCommands(projectRoot, opts = {}) {
|
|
|
5309
5692
|
return { installed: 0, updated: 0, skipped: 0, kept: 0, commandsDir: targetDir };
|
|
5310
5693
|
}
|
|
5311
5694
|
const framework = getConfig().framework;
|
|
5312
|
-
const
|
|
5695
|
+
const ctx = makeRenderContext(framework);
|
|
5696
|
+
const { vars: templateVars, resolveFragment, fragments: fragmentStats } = ctx;
|
|
5313
5697
|
const stats = runWithManifest(claudeDir, (manifest) => {
|
|
5314
5698
|
const syncStats = syncDirectory(
|
|
5315
5699
|
sourceDir,
|
|
@@ -5318,7 +5702,8 @@ function installCommands(projectRoot, opts = {}) {
|
|
|
5318
5702
|
manifest,
|
|
5319
5703
|
"commands",
|
|
5320
5704
|
true,
|
|
5321
|
-
templateVars
|
|
5705
|
+
templateVars,
|
|
5706
|
+
resolveFragment
|
|
5322
5707
|
);
|
|
5323
5708
|
if (!opts.skipPermissions) {
|
|
5324
5709
|
installPermissions(claudeDir, manifest, { silent: true });
|
|
@@ -5329,7 +5714,7 @@ function installCommands(projectRoot, opts = {}) {
|
|
|
5329
5714
|
}
|
|
5330
5715
|
function installAll(projectRoot, opts = {}) {
|
|
5331
5716
|
const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
|
|
5332
|
-
const claudeDir =
|
|
5717
|
+
const claudeDir = resolve9(projectRoot, claudeDirName);
|
|
5333
5718
|
const assets = {};
|
|
5334
5719
|
let totalInstalled = 0;
|
|
5335
5720
|
let totalUpdated = 0;
|
|
@@ -5337,12 +5722,13 @@ function installAll(projectRoot, opts = {}) {
|
|
|
5337
5722
|
let totalKept = 0;
|
|
5338
5723
|
let permissionsResult;
|
|
5339
5724
|
const framework = getConfig().framework;
|
|
5340
|
-
const
|
|
5725
|
+
const ctx = makeRenderContext(framework);
|
|
5726
|
+
const { vars: templateVars, resolveFragment, fragments: fragmentStats } = ctx;
|
|
5341
5727
|
runWithManifest(claudeDir, (manifest) => {
|
|
5342
5728
|
for (const assetType of ASSET_TYPES) {
|
|
5343
5729
|
const sourceDir = resolveAssetDir(assetType.name);
|
|
5344
5730
|
if (!sourceDir) continue;
|
|
5345
|
-
const targetDir =
|
|
5731
|
+
const targetDir = resolve9(claudeDir, assetType.targetSubdir);
|
|
5346
5732
|
const stats = syncDirectory(
|
|
5347
5733
|
sourceDir,
|
|
5348
5734
|
targetDir,
|
|
@@ -5350,7 +5736,8 @@ function installAll(projectRoot, opts = {}) {
|
|
|
5350
5736
|
manifest,
|
|
5351
5737
|
assetType.targetSubdir,
|
|
5352
5738
|
true,
|
|
5353
|
-
templateVars
|
|
5739
|
+
templateVars,
|
|
5740
|
+
resolveFragment
|
|
5354
5741
|
);
|
|
5355
5742
|
assets[assetType.name] = stats;
|
|
5356
5743
|
totalInstalled += stats.installed;
|
|
@@ -5369,6 +5756,7 @@ function installAll(projectRoot, opts = {}) {
|
|
|
5369
5756
|
totalSkipped,
|
|
5370
5757
|
totalKept,
|
|
5371
5758
|
claudeDir,
|
|
5759
|
+
fragments: fragmentStats,
|
|
5372
5760
|
permissions: permissionsResult
|
|
5373
5761
|
};
|
|
5374
5762
|
}
|
|
@@ -5420,16 +5808,15 @@ async function runInstallCommands() {
|
|
|
5420
5808
|
console.log(" Restart your Claude Code session to use them.");
|
|
5421
5809
|
console.log("");
|
|
5422
5810
|
}
|
|
5423
|
-
var
|
|
5811
|
+
var ASSET_TYPES, MANIFEST_VERSION, MANIFEST_RELPATH, MASSU_OWNED_PATHS;
|
|
5424
5812
|
var init_install_commands = __esm({
|
|
5425
5813
|
"src/commands/install-commands.ts"() {
|
|
5426
5814
|
"use strict";
|
|
5427
5815
|
init_config();
|
|
5428
|
-
|
|
5816
|
+
init_asset_resolution();
|
|
5817
|
+
init_asset_render();
|
|
5429
5818
|
init_settings_local();
|
|
5430
5819
|
init_permissions();
|
|
5431
|
-
__filename = fileURLToPath2(import.meta.url);
|
|
5432
|
-
__dirname = dirname9(__filename);
|
|
5433
5820
|
ASSET_TYPES = [
|
|
5434
5821
|
{ name: "commands", targetSubdir: "commands", description: "slash commands" },
|
|
5435
5822
|
{ name: "agents", targetSubdir: "agents", description: "agent definitions" },
|
|
@@ -5440,14 +5827,6 @@ var init_install_commands = __esm({
|
|
|
5440
5827
|
MANIFEST_VERSION = 1;
|
|
5441
5828
|
MANIFEST_RELPATH = join8(".massu", "install-manifest.json");
|
|
5442
5829
|
MASSU_OWNED_PATHS = ["commands/_verification-laws.md"];
|
|
5443
|
-
PASSTHROUGH_LANG_KEYS = [
|
|
5444
|
-
"typescript",
|
|
5445
|
-
"javascript",
|
|
5446
|
-
"python",
|
|
5447
|
-
"swift",
|
|
5448
|
-
"rust",
|
|
5449
|
-
"go"
|
|
5450
|
-
];
|
|
5451
5830
|
}
|
|
5452
5831
|
});
|
|
5453
5832
|
|
|
@@ -5651,17 +6030,17 @@ var init_manifest_registry = __esm({
|
|
|
5651
6030
|
});
|
|
5652
6031
|
|
|
5653
6032
|
// src/detect/package-detector.ts
|
|
5654
|
-
import { readFileSync as
|
|
6033
|
+
import { readFileSync as readFileSync11, existsSync as existsSync15, statSync as statSync5, lstatSync, readdirSync as readdirSync6 } from "fs";
|
|
5655
6034
|
import { join as join9, relative as relative2 } from "path";
|
|
5656
6035
|
import { parse as parseToml } from "smol-toml";
|
|
5657
6036
|
function safeRead(path) {
|
|
5658
6037
|
try {
|
|
5659
|
-
if (!
|
|
6038
|
+
if (!existsSync15(path)) return null;
|
|
5660
6039
|
const ls = lstatSync(path);
|
|
5661
6040
|
if (ls.isSymbolicLink()) return null;
|
|
5662
|
-
const st =
|
|
6041
|
+
const st = statSync5(path);
|
|
5663
6042
|
if (!st.isFile()) return null;
|
|
5664
|
-
return
|
|
6043
|
+
return readFileSync11(path, "utf-8");
|
|
5665
6044
|
} catch {
|
|
5666
6045
|
return null;
|
|
5667
6046
|
}
|
|
@@ -5692,7 +6071,7 @@ function parsePackageJson(path, directory, root, warnings) {
|
|
|
5692
6071
|
const peer = Object.keys(
|
|
5693
6072
|
pkg.peerDependencies ?? {}
|
|
5694
6073
|
);
|
|
5695
|
-
const hasTs = deps.includes("typescript") || devDeps.includes("typescript") ||
|
|
6074
|
+
const hasTs = deps.includes("typescript") || devDeps.includes("typescript") || existsSync15(join9(directory, "tsconfig.json"));
|
|
5696
6075
|
const language = hasTs ? "typescript" : "javascript";
|
|
5697
6076
|
const scripts = Object.keys(
|
|
5698
6077
|
pkg.scripts ?? {}
|
|
@@ -6119,13 +6498,13 @@ function detectManifestsInDir(dir, root, warnings) {
|
|
|
6119
6498
|
for (const entry of getManifestRegistry2()) {
|
|
6120
6499
|
if (!entry.pattern.startsWith("*")) {
|
|
6121
6500
|
const path = join9(dir, entry.pattern);
|
|
6122
|
-
if (!
|
|
6501
|
+
if (!existsSync15(path)) continue;
|
|
6123
6502
|
const m3 = entry.parse(path, dir, root, warnings);
|
|
6124
6503
|
if (m3 !== null) out.push(m3);
|
|
6125
6504
|
} else {
|
|
6126
6505
|
if (dirEntries === null) {
|
|
6127
6506
|
try {
|
|
6128
|
-
dirEntries =
|
|
6507
|
+
dirEntries = readdirSync6(dir);
|
|
6129
6508
|
} catch {
|
|
6130
6509
|
dirEntries = [];
|
|
6131
6510
|
}
|
|
@@ -6133,7 +6512,7 @@ function detectManifestsInDir(dir, root, warnings) {
|
|
|
6133
6512
|
for (const fname of dirEntries) {
|
|
6134
6513
|
if (!matchManifestPattern2(fname, entry.pattern)) continue;
|
|
6135
6514
|
const path = join9(dir, fname);
|
|
6136
|
-
if (!
|
|
6515
|
+
if (!existsSync15(path)) continue;
|
|
6137
6516
|
const m3 = entry.parse(path, dir, root, warnings);
|
|
6138
6517
|
if (m3 !== null) out.push(m3);
|
|
6139
6518
|
}
|
|
@@ -6143,7 +6522,7 @@ function detectManifestsInDir(dir, root, warnings) {
|
|
|
6143
6522
|
}
|
|
6144
6523
|
function listSubdirs(dir) {
|
|
6145
6524
|
try {
|
|
6146
|
-
return
|
|
6525
|
+
return readdirSync6(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS.has(e2.name)).map((e2) => join9(dir, e2.name));
|
|
6147
6526
|
} catch {
|
|
6148
6527
|
return [];
|
|
6149
6528
|
}
|
|
@@ -6154,7 +6533,7 @@ function detectPackageManifests(projectRoot) {
|
|
|
6154
6533
|
manifests.push(...detectManifestsInDir(projectRoot, projectRoot, warnings));
|
|
6155
6534
|
for (const ws of WORKSPACE_DIRS) {
|
|
6156
6535
|
const wsRoot = join9(projectRoot, ws);
|
|
6157
|
-
if (!
|
|
6536
|
+
if (!existsSync15(wsRoot)) continue;
|
|
6158
6537
|
for (const sub of listSubdirs(wsRoot)) {
|
|
6159
6538
|
manifests.push(...detectManifestsInDir(sub, projectRoot, warnings));
|
|
6160
6539
|
for (const sub2 of listSubdirs(sub)) {
|
|
@@ -6406,22 +6785,22 @@ import {
|
|
|
6406
6785
|
openSync as openSync3,
|
|
6407
6786
|
fsyncSync as fsyncSync3,
|
|
6408
6787
|
closeSync as closeSync3,
|
|
6409
|
-
existsSync as
|
|
6788
|
+
existsSync as existsSync16,
|
|
6410
6789
|
realpathSync as realpathSync2,
|
|
6411
6790
|
unlinkSync as unlinkSync2,
|
|
6412
6791
|
accessSync as accessSync2,
|
|
6413
|
-
statSync as
|
|
6792
|
+
statSync as statSync6,
|
|
6414
6793
|
chmodSync as chmodSync3,
|
|
6415
6794
|
constants
|
|
6416
6795
|
} from "fs";
|
|
6417
|
-
import { dirname as dirname10, resolve as
|
|
6796
|
+
import { dirname as dirname10, resolve as resolve10, relative as relative3, isAbsolute, basename as basename3, sep } from "path";
|
|
6418
6797
|
import { createHash as createHash4 } from "crypto";
|
|
6419
6798
|
function assertContainedIn(rootDir, candidate, opts = {}) {
|
|
6420
6799
|
if (candidate.includes("\0")) {
|
|
6421
6800
|
throw new PathEscapeError("path contains a NUL byte");
|
|
6422
6801
|
}
|
|
6423
|
-
const root =
|
|
6424
|
-
const abs =
|
|
6802
|
+
const root = resolve10(rootDir);
|
|
6803
|
+
const abs = resolve10(root, candidate);
|
|
6425
6804
|
if (!opts.allowNested) {
|
|
6426
6805
|
const rel = relative3(root, abs);
|
|
6427
6806
|
if (rel.includes(sep)) {
|
|
@@ -6440,13 +6819,13 @@ function assertContainedIn(rootDir, candidate, opts = {}) {
|
|
|
6440
6819
|
}
|
|
6441
6820
|
function isContainedIn(rootDir, candidate, opts = {}) {
|
|
6442
6821
|
if (candidate.includes("\0")) return false;
|
|
6443
|
-
const root =
|
|
6444
|
-
const abs =
|
|
6822
|
+
const root = resolve10(rootDir);
|
|
6823
|
+
const abs = resolve10(root, candidate);
|
|
6445
6824
|
const rel = relative3(root, abs);
|
|
6446
6825
|
if (rel === "" && !opts.allowRoot) return false;
|
|
6447
6826
|
if (rel.startsWith("..") || isAbsolute(rel)) return false;
|
|
6448
6827
|
let probe = abs;
|
|
6449
|
-
while (!
|
|
6828
|
+
while (!existsSync16(probe) && probe.length > 1) {
|
|
6450
6829
|
const parent = dirname10(probe);
|
|
6451
6830
|
if (parent === probe) break;
|
|
6452
6831
|
probe = parent;
|
|
@@ -6465,11 +6844,11 @@ function isContainedIn(rootDir, candidate, opts = {}) {
|
|
|
6465
6844
|
}
|
|
6466
6845
|
function atomicWriteFileSync(destPath, contents) {
|
|
6467
6846
|
const dir = dirname10(destPath);
|
|
6468
|
-
const tmp =
|
|
6847
|
+
const tmp = resolve10(dir, `.${basename3(destPath)}.massu-tmp-${process.pid}`);
|
|
6469
6848
|
let destMode;
|
|
6470
|
-
if (
|
|
6849
|
+
if (existsSync16(destPath)) {
|
|
6471
6850
|
accessSync2(destPath, constants.W_OK);
|
|
6472
|
-
destMode =
|
|
6851
|
+
destMode = statSync6(destPath).mode & 511;
|
|
6473
6852
|
}
|
|
6474
6853
|
let fd;
|
|
6475
6854
|
try {
|
|
@@ -6488,7 +6867,7 @@ function atomicWriteFileSync(destPath, contents) {
|
|
|
6488
6867
|
}
|
|
6489
6868
|
}
|
|
6490
6869
|
try {
|
|
6491
|
-
if (
|
|
6870
|
+
if (existsSync16(tmp)) unlinkSync2(tmp);
|
|
6492
6871
|
} catch {
|
|
6493
6872
|
}
|
|
6494
6873
|
throw err;
|
|
@@ -10422,11 +10801,11 @@ var require_out = __commonJS({
|
|
|
10422
10801
|
async.read(path, getSettings(optionsOrSettingsOrCallback), callback);
|
|
10423
10802
|
}
|
|
10424
10803
|
exports.stat = stat;
|
|
10425
|
-
function
|
|
10804
|
+
function statSync23(path, optionsOrSettings) {
|
|
10426
10805
|
const settings = getSettings(optionsOrSettings);
|
|
10427
10806
|
return sync.read(path, settings);
|
|
10428
10807
|
}
|
|
10429
|
-
exports.statSync =
|
|
10808
|
+
exports.statSync = statSync23;
|
|
10430
10809
|
function getSettings(settingsOrOptions = {}) {
|
|
10431
10810
|
if (settingsOrOptions instanceof settings_1.default) {
|
|
10432
10811
|
return settingsOrOptions;
|
|
@@ -11091,41 +11470,41 @@ var require_queue = __commonJS({
|
|
|
11091
11470
|
queue.drained = drained;
|
|
11092
11471
|
return queue;
|
|
11093
11472
|
function push(value) {
|
|
11094
|
-
var p19 = new Promise(function(
|
|
11473
|
+
var p19 = new Promise(function(resolve49, reject) {
|
|
11095
11474
|
pushCb(value, function(err, result) {
|
|
11096
11475
|
if (err) {
|
|
11097
11476
|
reject(err);
|
|
11098
11477
|
return;
|
|
11099
11478
|
}
|
|
11100
|
-
|
|
11479
|
+
resolve49(result);
|
|
11101
11480
|
});
|
|
11102
11481
|
});
|
|
11103
11482
|
p19.catch(noop);
|
|
11104
11483
|
return p19;
|
|
11105
11484
|
}
|
|
11106
11485
|
function unshift(value) {
|
|
11107
|
-
var p19 = new Promise(function(
|
|
11486
|
+
var p19 = new Promise(function(resolve49, reject) {
|
|
11108
11487
|
unshiftCb(value, function(err, result) {
|
|
11109
11488
|
if (err) {
|
|
11110
11489
|
reject(err);
|
|
11111
11490
|
return;
|
|
11112
11491
|
}
|
|
11113
|
-
|
|
11492
|
+
resolve49(result);
|
|
11114
11493
|
});
|
|
11115
11494
|
});
|
|
11116
11495
|
p19.catch(noop);
|
|
11117
11496
|
return p19;
|
|
11118
11497
|
}
|
|
11119
11498
|
function drained() {
|
|
11120
|
-
var p19 = new Promise(function(
|
|
11499
|
+
var p19 = new Promise(function(resolve49) {
|
|
11121
11500
|
process.nextTick(function() {
|
|
11122
11501
|
if (queue.idle()) {
|
|
11123
|
-
|
|
11502
|
+
resolve49();
|
|
11124
11503
|
} else {
|
|
11125
11504
|
var previousDrain = queue.drain;
|
|
11126
11505
|
queue.drain = function() {
|
|
11127
11506
|
if (typeof previousDrain === "function") previousDrain();
|
|
11128
|
-
|
|
11507
|
+
resolve49();
|
|
11129
11508
|
queue.drain = previousDrain;
|
|
11130
11509
|
};
|
|
11131
11510
|
}
|
|
@@ -11611,9 +11990,9 @@ var require_stream3 = __commonJS({
|
|
|
11611
11990
|
});
|
|
11612
11991
|
}
|
|
11613
11992
|
_getStat(filepath) {
|
|
11614
|
-
return new Promise((
|
|
11993
|
+
return new Promise((resolve49, reject) => {
|
|
11615
11994
|
this._stat(filepath, this._fsStatSettings, (error, stats) => {
|
|
11616
|
-
return error === null ?
|
|
11995
|
+
return error === null ? resolve49(stats) : reject(error);
|
|
11617
11996
|
});
|
|
11618
11997
|
});
|
|
11619
11998
|
}
|
|
@@ -11637,10 +12016,10 @@ var require_async5 = __commonJS({
|
|
|
11637
12016
|
this._readerStream = new stream_1.default(this._settings);
|
|
11638
12017
|
}
|
|
11639
12018
|
dynamic(root, options) {
|
|
11640
|
-
return new Promise((
|
|
12019
|
+
return new Promise((resolve49, reject) => {
|
|
11641
12020
|
this._walkAsync(root, options, (error, entries) => {
|
|
11642
12021
|
if (error === null) {
|
|
11643
|
-
|
|
12022
|
+
resolve49(entries);
|
|
11644
12023
|
} else {
|
|
11645
12024
|
reject(error);
|
|
11646
12025
|
}
|
|
@@ -11650,10 +12029,10 @@ var require_async5 = __commonJS({
|
|
|
11650
12029
|
async static(patterns, options) {
|
|
11651
12030
|
const entries = [];
|
|
11652
12031
|
const stream = this._readerStream.static(patterns, options);
|
|
11653
|
-
return new Promise((
|
|
12032
|
+
return new Promise((resolve49, reject) => {
|
|
11654
12033
|
stream.once("error", reject);
|
|
11655
12034
|
stream.on("data", (entry) => entries.push(entry));
|
|
11656
|
-
stream.once("end", () =>
|
|
12035
|
+
stream.once("end", () => resolve49(entries));
|
|
11657
12036
|
});
|
|
11658
12037
|
}
|
|
11659
12038
|
};
|
|
@@ -12499,25 +12878,25 @@ var init_source_dir_detector = __esm({
|
|
|
12499
12878
|
});
|
|
12500
12879
|
|
|
12501
12880
|
// src/detect/monorepo-detector.ts
|
|
12502
|
-
import { readFileSync as
|
|
12881
|
+
import { readFileSync as readFileSync12, existsSync as existsSync17, statSync as statSync7, lstatSync as lstatSync2, readdirSync as readdirSync7 } from "fs";
|
|
12503
12882
|
import { join as join10, relative as relative4 } from "path";
|
|
12504
12883
|
import { parse as parseYaml3 } from "yaml";
|
|
12505
12884
|
import { parse as parseToml2 } from "smol-toml";
|
|
12506
12885
|
function safeReadText(path) {
|
|
12507
12886
|
try {
|
|
12508
|
-
if (!
|
|
12887
|
+
if (!existsSync17(path)) return null;
|
|
12509
12888
|
const ls = lstatSync2(path);
|
|
12510
12889
|
if (ls.isSymbolicLink()) return null;
|
|
12511
|
-
const st =
|
|
12890
|
+
const st = statSync7(path);
|
|
12512
12891
|
if (!st.isFile()) return null;
|
|
12513
|
-
return
|
|
12892
|
+
return readFileSync12(path, "utf-8");
|
|
12514
12893
|
} catch {
|
|
12515
12894
|
return null;
|
|
12516
12895
|
}
|
|
12517
12896
|
}
|
|
12518
12897
|
function firstManifestIn(dir) {
|
|
12519
12898
|
for (const m3 of MANIFEST_PRIORITY) {
|
|
12520
|
-
if (
|
|
12899
|
+
if (existsSync17(join10(dir, m3))) return m3;
|
|
12521
12900
|
}
|
|
12522
12901
|
return null;
|
|
12523
12902
|
}
|
|
@@ -12573,7 +12952,7 @@ function pkgFromDir(root, dir) {
|
|
|
12573
12952
|
}
|
|
12574
12953
|
function listSubdirs2(dir) {
|
|
12575
12954
|
try {
|
|
12576
|
-
return
|
|
12955
|
+
return readdirSync7(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS2.has(e2.name)).map((e2) => join10(dir, e2.name));
|
|
12577
12956
|
} catch {
|
|
12578
12957
|
return [];
|
|
12579
12958
|
}
|
|
@@ -12582,7 +12961,7 @@ function genericWorkspaces(root) {
|
|
|
12582
12961
|
const out = [];
|
|
12583
12962
|
for (const parent of CONVENTIONAL_WORKSPACE_PARENTS) {
|
|
12584
12963
|
const p19 = join10(root, parent);
|
|
12585
|
-
if (!
|
|
12964
|
+
if (!existsSync17(p19)) continue;
|
|
12586
12965
|
for (const sub of listSubdirs2(p19)) {
|
|
12587
12966
|
const pkg = pkgFromDir(root, sub);
|
|
12588
12967
|
if (pkg) out.push(pkg);
|
|
@@ -12623,7 +13002,7 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
12623
13002
|
const parts = pattern.split("/");
|
|
12624
13003
|
if (parts.length === 2 && (parts[1] === "*" || parts[1] === "**")) {
|
|
12625
13004
|
const parent = join10(root, parts[0]);
|
|
12626
|
-
if (!
|
|
13005
|
+
if (!existsSync17(parent)) continue;
|
|
12627
13006
|
for (const sub of listSubdirs2(parent)) {
|
|
12628
13007
|
const pkg = pkgFromDir(root, sub);
|
|
12629
13008
|
if (pkg && !seen.has(pkg.path)) {
|
|
@@ -12634,7 +13013,7 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
12634
13013
|
continue;
|
|
12635
13014
|
}
|
|
12636
13015
|
const direct = join10(root, pattern);
|
|
12637
|
-
if (
|
|
13016
|
+
if (existsSync17(direct)) {
|
|
12638
13017
|
const pkg = pkgFromDir(root, direct);
|
|
12639
13018
|
if (pkg && !seen.has(pkg.path)) {
|
|
12640
13019
|
seen.add(pkg.path);
|
|
@@ -12645,16 +13024,16 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
12645
13024
|
return out;
|
|
12646
13025
|
}
|
|
12647
13026
|
function hasTurbo(root) {
|
|
12648
|
-
return
|
|
13027
|
+
return existsSync17(join10(root, "turbo.json"));
|
|
12649
13028
|
}
|
|
12650
13029
|
function hasNx(root) {
|
|
12651
|
-
return
|
|
13030
|
+
return existsSync17(join10(root, "nx.json"));
|
|
12652
13031
|
}
|
|
12653
13032
|
function hasLerna(root) {
|
|
12654
|
-
return
|
|
13033
|
+
return existsSync17(join10(root, "lerna.json"));
|
|
12655
13034
|
}
|
|
12656
13035
|
function hasBazel(root) {
|
|
12657
|
-
return
|
|
13036
|
+
return existsSync17(join10(root, "WORKSPACE")) || existsSync17(join10(root, "WORKSPACE.bazel")) || existsSync17(join10(root, "MODULE.bazel"));
|
|
12658
13037
|
}
|
|
12659
13038
|
function detectMonorepo(projectRoot) {
|
|
12660
13039
|
const nested = [];
|
|
@@ -12753,7 +13132,11 @@ function defaultsFor(language, fw, dir) {
|
|
|
12753
13132
|
test: testFw === "unittest" ? prefix(dir, "python3 -m unittest") : prefix(dir, "python3 -m pytest -q"),
|
|
12754
13133
|
type: prefix(dir, "python3 -m mypy ."),
|
|
12755
13134
|
build: null,
|
|
12756
|
-
|
|
13135
|
+
// `python3 -m py_compile` with NO filenames exits 2 ('the following arguments are
|
|
13136
|
+
// required: filenames') — a check that can never pass, i.e. a brick, and a brick
|
|
13137
|
+
// gets bypassed. `compileall -q .` measured exit 1 on a tree with a syntax error
|
|
13138
|
+
// and exit 0 on a clean tree (2026-09-20).
|
|
13139
|
+
syntax: prefix(dir, "python3 -m compileall -q ."),
|
|
12757
13140
|
lint: prefix(dir, "python3 -m ruff check .")
|
|
12758
13141
|
};
|
|
12759
13142
|
}
|
|
@@ -12791,7 +13174,12 @@ function defaultsFor(language, fw, dir) {
|
|
|
12791
13174
|
return {
|
|
12792
13175
|
test: prefix(dir, "swift test"),
|
|
12793
13176
|
type: prefix(dir, "swift build"),
|
|
12794
|
-
build
|
|
13177
|
+
// `xcodebuild build` requires an .xcodeproj/.xcworkspace, but this block's other
|
|
13178
|
+
// commands (`swift test`, `swift build`) and the swift-ios template's declared
|
|
13179
|
+
// layout (Sources/) are SwiftPM. One stack cannot be SwiftPM for two keys and
|
|
13180
|
+
// Xcode for a third. Operator-approved 2026-09-20; the three anti-vacuity plants
|
|
13181
|
+
// that target this literal were re-pointed in the same change.
|
|
13182
|
+
build: prefix(dir, "swift build -c release"),
|
|
12795
13183
|
syntax: null,
|
|
12796
13184
|
lint: prefix(dir, "swiftlint")
|
|
12797
13185
|
};
|
|
@@ -12816,10 +13204,17 @@ function defaultsFor(language, fw, dir) {
|
|
|
12816
13204
|
}
|
|
12817
13205
|
case "ruby": {
|
|
12818
13206
|
return {
|
|
12819
|
-
test
|
|
13207
|
+
// Minitest is Rails' default; `bin/rails test` works on a fresh `rails new`.
|
|
13208
|
+
// `bundle exec rspec` requires adding rspec-rails to the Gemfile first.
|
|
13209
|
+
// https://guides.rubyonrails.org/testing.html
|
|
13210
|
+
test: prefix(dir, "bin/rails test"),
|
|
12820
13211
|
type: null,
|
|
12821
13212
|
build: null,
|
|
12822
|
-
|
|
13213
|
+
// `ruby -c` with NO file argument reads STDIN: on empty stdin it prints
|
|
13214
|
+
// 'Syntax OK' and exits 0 without inspecting one project file, and it BLOCKS on a
|
|
13215
|
+
// TTY. A check that cannot fail is a blind gate. Measured 2026-09-20; the
|
|
13216
|
+
// replacement exits 1 on a tree with a syntax error and 0 on a clean one.
|
|
13217
|
+
syntax: prefix(dir, "find app lib -name '*.rb' -print0 | xargs -0 -n1 ruby -c > /dev/null"),
|
|
12823
13218
|
lint: prefix(dir, "bundle exec rubocop")
|
|
12824
13219
|
};
|
|
12825
13220
|
}
|
|
@@ -12845,7 +13240,7 @@ var init_vr_command_map = __esm({
|
|
|
12845
13240
|
});
|
|
12846
13241
|
|
|
12847
13242
|
// src/detect/domain-inferrer.ts
|
|
12848
|
-
import { existsSync as
|
|
13243
|
+
import { existsSync as existsSync18, readdirSync as readdirSync8 } from "fs";
|
|
12849
13244
|
import { join as join11 } from "path";
|
|
12850
13245
|
function titleCase(s) {
|
|
12851
13246
|
if (!s) return s;
|
|
@@ -12867,9 +13262,9 @@ function topLevelSrcSubdirs(root, sourceDirs) {
|
|
|
12867
13262
|
const seen = /* @__PURE__ */ new Set();
|
|
12868
13263
|
for (const rel of effective) {
|
|
12869
13264
|
const abs = join11(root, rel);
|
|
12870
|
-
if (!
|
|
13265
|
+
if (!existsSync18(abs)) continue;
|
|
12871
13266
|
try {
|
|
12872
|
-
for (const e2 of
|
|
13267
|
+
for (const e2 of readdirSync8(abs, { withFileTypes: true })) {
|
|
12873
13268
|
if (!e2.isDirectory()) continue;
|
|
12874
13269
|
if (IGNORED_SUBDIRS.has(e2.name)) continue;
|
|
12875
13270
|
seen.add(e2.name);
|
|
@@ -12954,8 +13349,8 @@ var init_domain_inferrer = __esm({
|
|
|
12954
13349
|
});
|
|
12955
13350
|
|
|
12956
13351
|
// src/detect/regex-fallback.ts
|
|
12957
|
-
import { existsSync as
|
|
12958
|
-
import { resolve as
|
|
13352
|
+
import { existsSync as existsSync19, readdirSync as readdirSync9, readFileSync as readFileSync13, statSync as statSync8 } from "fs";
|
|
13353
|
+
import { resolve as resolve11, join as join12, basename as basename4 } from "path";
|
|
12959
13354
|
function introspectPython(detection, projectRoot) {
|
|
12960
13355
|
const sourceDir = resolveSourceDir(detection, "python", projectRoot);
|
|
12961
13356
|
if (!sourceDir) return null;
|
|
@@ -13121,10 +13516,10 @@ function resolveSourceDir(detection, lang, projectRoot) {
|
|
|
13121
13516
|
const list = info?.source_dirs ?? [];
|
|
13122
13517
|
if (list.length > 0) {
|
|
13123
13518
|
const first = list[0];
|
|
13124
|
-
const abs =
|
|
13125
|
-
return
|
|
13519
|
+
const abs = resolve11(projectRoot, first);
|
|
13520
|
+
return existsSync19(abs) ? abs : null;
|
|
13126
13521
|
}
|
|
13127
|
-
return
|
|
13522
|
+
return existsSync19(projectRoot) ? projectRoot : null;
|
|
13128
13523
|
}
|
|
13129
13524
|
function sampleFiles(dir, nameRegex, pathFilter) {
|
|
13130
13525
|
const out = [];
|
|
@@ -13134,7 +13529,7 @@ function sampleFiles(dir, nameRegex, pathFilter) {
|
|
|
13134
13529
|
if (depth > MAX_DIR_DEPTH) continue;
|
|
13135
13530
|
let entries;
|
|
13136
13531
|
try {
|
|
13137
|
-
entries =
|
|
13532
|
+
entries = readdirSync9(path);
|
|
13138
13533
|
} catch {
|
|
13139
13534
|
continue;
|
|
13140
13535
|
}
|
|
@@ -13147,7 +13542,7 @@ function sampleFiles(dir, nameRegex, pathFilter) {
|
|
|
13147
13542
|
const child = join12(path, entry);
|
|
13148
13543
|
let st;
|
|
13149
13544
|
try {
|
|
13150
|
-
st =
|
|
13545
|
+
st = statSync8(child);
|
|
13151
13546
|
} catch {
|
|
13152
13547
|
continue;
|
|
13153
13548
|
}
|
|
@@ -13167,9 +13562,9 @@ function sampleFiles(dir, nameRegex, pathFilter) {
|
|
|
13167
13562
|
}
|
|
13168
13563
|
function readSafe(path) {
|
|
13169
13564
|
try {
|
|
13170
|
-
const st =
|
|
13565
|
+
const st = statSync8(path);
|
|
13171
13566
|
if (st.size > MAX_FILE_BYTES) return null;
|
|
13172
|
-
return
|
|
13567
|
+
return readFileSync13(path, "utf-8");
|
|
13173
13568
|
} catch {
|
|
13174
13569
|
return null;
|
|
13175
13570
|
}
|
|
@@ -13254,7 +13649,7 @@ var init_parse_guard = __esm({
|
|
|
13254
13649
|
|
|
13255
13650
|
// src/detect/adapters/runner.ts
|
|
13256
13651
|
import { basename as basename5, relative as relative5 } from "path";
|
|
13257
|
-
import { existsSync as
|
|
13652
|
+
import { existsSync as existsSync20, readdirSync as readdirSync10, readFileSync as readFileSync14, statSync as statSync9 } from "fs";
|
|
13258
13653
|
import { join as join13 } from "path";
|
|
13259
13654
|
async function runAdapters(adapters, rootDir, signals, options = {}) {
|
|
13260
13655
|
const out = {
|
|
@@ -13349,10 +13744,10 @@ function buildDetectionSignals(rootDir) {
|
|
|
13349
13744
|
const presentDirs = /* @__PURE__ */ new Set();
|
|
13350
13745
|
const presentFiles = /* @__PURE__ */ new Set();
|
|
13351
13746
|
try {
|
|
13352
|
-
for (const entry of
|
|
13747
|
+
for (const entry of readdirSync10(rootDir)) {
|
|
13353
13748
|
if (entry.startsWith(".")) continue;
|
|
13354
13749
|
try {
|
|
13355
|
-
const st =
|
|
13750
|
+
const st = statSync9(join13(rootDir, entry));
|
|
13356
13751
|
if (st.isDirectory()) presentDirs.add(entry);
|
|
13357
13752
|
else if (st.isFile()) presentFiles.add(entry);
|
|
13358
13753
|
} catch {
|
|
@@ -13389,9 +13784,9 @@ function tryReadGradleBuild(rootDir, presentFiles) {
|
|
|
13389
13784
|
return void 0;
|
|
13390
13785
|
}
|
|
13391
13786
|
function tryReadString(path) {
|
|
13392
|
-
if (!
|
|
13787
|
+
if (!existsSync20(path)) return void 0;
|
|
13393
13788
|
try {
|
|
13394
|
-
return
|
|
13789
|
+
return readFileSync14(path, "utf-8");
|
|
13395
13790
|
} catch {
|
|
13396
13791
|
return void 0;
|
|
13397
13792
|
}
|
|
@@ -13509,8 +13904,8 @@ ${querySource}`
|
|
|
13509
13904
|
import { createHash as createHash5 } from "crypto";
|
|
13510
13905
|
import {
|
|
13511
13906
|
mkdirSync as mkdirSync9,
|
|
13512
|
-
readdirSync as
|
|
13513
|
-
readFileSync as
|
|
13907
|
+
readdirSync as readdirSync11,
|
|
13908
|
+
readFileSync as readFileSync15,
|
|
13514
13909
|
writeFileSync as writeFileSync4,
|
|
13515
13910
|
renameSync as renameSync3,
|
|
13516
13911
|
unlinkSync as unlinkSync3,
|
|
@@ -13546,7 +13941,7 @@ function evictBeyondRetainCount(retain = getCacheRetainCount()) {
|
|
|
13546
13941
|
const dir = getCacheDir();
|
|
13547
13942
|
let entries;
|
|
13548
13943
|
try {
|
|
13549
|
-
entries =
|
|
13944
|
+
entries = readdirSync11(dir);
|
|
13550
13945
|
} catch {
|
|
13551
13946
|
return;
|
|
13552
13947
|
}
|
|
@@ -13610,7 +14005,7 @@ async function loadGrammar(language, options = {}) {
|
|
|
13610
14005
|
}
|
|
13611
14006
|
let bytes;
|
|
13612
14007
|
try {
|
|
13613
|
-
bytes =
|
|
14008
|
+
bytes = readFileSync15(cachePath);
|
|
13614
14009
|
} catch (e2) {
|
|
13615
14010
|
bytes = new Uint8Array(0);
|
|
13616
14011
|
}
|
|
@@ -14528,7 +14923,7 @@ __export(file_sampler_exports, {
|
|
|
14528
14923
|
SAMPLE_TEST_FILE_PATTERNS: () => SAMPLE_TEST_FILE_PATTERNS,
|
|
14529
14924
|
sampleFilesForAdapter: () => sampleFilesForAdapter
|
|
14530
14925
|
});
|
|
14531
|
-
import { readdirSync as
|
|
14926
|
+
import { readdirSync as readdirSync12, readFileSync as readFileSync16, lstatSync as lstatSync4 } from "node:fs";
|
|
14532
14927
|
import { join as join15, extname } from "node:path";
|
|
14533
14928
|
function sampleFilesForAdapter(adapter, projectRoot, detection, options = {}) {
|
|
14534
14929
|
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
@@ -14560,7 +14955,7 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
|
|
|
14560
14955
|
if (out.length >= maxFiles) return;
|
|
14561
14956
|
let entries;
|
|
14562
14957
|
try {
|
|
14563
|
-
entries =
|
|
14958
|
+
entries = readdirSync12(dir);
|
|
14564
14959
|
} catch {
|
|
14565
14960
|
return;
|
|
14566
14961
|
}
|
|
@@ -14589,7 +14984,7 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
|
|
|
14589
14984
|
seen.add(fullPath);
|
|
14590
14985
|
let content;
|
|
14591
14986
|
try {
|
|
14592
|
-
content =
|
|
14987
|
+
content = readFileSync16(fullPath, "utf-8");
|
|
14593
14988
|
} catch {
|
|
14594
14989
|
continue;
|
|
14595
14990
|
}
|
|
@@ -15201,7 +15596,7 @@ function cD({ input: t2 = $, output: u2 = j, overwrite: F = true, hideCursor: e2
|
|
|
15201
15596
|
t2.off("keypress", i), e2 && u2.write(import_sisteransi.cursor.show), t2.isTTY && !xD && t2.setRawMode(false), s.terminal = false, s.close();
|
|
15202
15597
|
};
|
|
15203
15598
|
}
|
|
15204
|
-
var import_sisteransi, import_picocolors, Q, P, X, DD, uD, FD, m, L, N, I, r, tD, eD, iD, v, CD, w, W, rD, R, y, V,
|
|
15599
|
+
var import_sisteransi, import_picocolors, Q, P, X, DD, uD, FD, m, L, N, I, r, tD, eD, iD, v, CD, w, W, rD, R, y, V, z4, ED, _, nD, oD, aD, c, xD, S, AD, pD, h, x, fD, gD, vD, K, dD, bD, mD, Y, wD, yD, _D, Z, kD, SD, $D, q, jD, MD, TD, H, OD, PD;
|
|
15205
15600
|
var init_dist = __esm({
|
|
15206
15601
|
"../../node_modules/@clack/core/dist/index.mjs"() {
|
|
15207
15602
|
import_sisteransi = __toESM(require_src(), 1);
|
|
@@ -15258,7 +15653,7 @@ var init_dist = __esm({
|
|
|
15258
15653
|
R = "m";
|
|
15259
15654
|
y = `${rD}8;;`;
|
|
15260
15655
|
V = (t2) => `${v.values().next().value}${W}${t2}${R}`;
|
|
15261
|
-
|
|
15656
|
+
z4 = (t2) => `${v.values().next().value}${y}${t2}${w}`;
|
|
15262
15657
|
ED = (t2) => t2.split(" ").map((u2) => A(u2));
|
|
15263
15658
|
_ = (t2, u2, F) => {
|
|
15264
15659
|
const e2 = [...u2];
|
|
@@ -15318,8 +15713,8 @@ var init_dist = __esm({
|
|
|
15318
15713
|
}
|
|
15319
15714
|
const n = iD.codes.get(Number(s));
|
|
15320
15715
|
o[E2 + 1] === `
|
|
15321
|
-
` ? (i && (e2 +=
|
|
15322
|
-
` && (s && n && (e2 += V(s)), i && (e2 +=
|
|
15716
|
+
` ? (i && (e2 += z4("")), s && n && (e2 += V(n))) : a2 === `
|
|
15717
|
+
` && (s && n && (e2 += V(s)), i && (e2 += z4(i)));
|
|
15323
15718
|
}
|
|
15324
15719
|
return e2;
|
|
15325
15720
|
};
|
|
@@ -16022,8 +16417,8 @@ __export(init_exports, {
|
|
|
16022
16417
|
validateWrittenConfig: () => validateWrittenConfig,
|
|
16023
16418
|
writeConfigAtomic: () => writeConfigAtomic
|
|
16024
16419
|
});
|
|
16025
|
-
import { closeSync as closeSync4, existsSync as
|
|
16026
|
-
import { resolve as
|
|
16420
|
+
import { closeSync as closeSync4, existsSync as existsSync21, fsyncSync as fsyncSync4, openSync as openSync4, readFileSync as readFileSync17, writeFileSync as writeFileSync5, writeSync as writeSync2, mkdirSync as mkdirSync10, readdirSync as readdirSync13, renameSync as renameSync4, rmSync as rmSync6, statSync as statSync11, chmodSync as chmodSync5 } from "fs";
|
|
16421
|
+
import { resolve as resolve12, basename as basename6, dirname as dirname12 } from "path";
|
|
16027
16422
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
16028
16423
|
import { homedir as homedir7 } from "os";
|
|
16029
16424
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -16035,10 +16430,10 @@ function detectFramework(projectRoot) {
|
|
|
16035
16430
|
orm: "none",
|
|
16036
16431
|
ui: "none"
|
|
16037
16432
|
};
|
|
16038
|
-
const pkgPath =
|
|
16039
|
-
if (!
|
|
16433
|
+
const pkgPath = resolve12(projectRoot, "package.json");
|
|
16434
|
+
if (!existsSync21(pkgPath)) return result;
|
|
16040
16435
|
try {
|
|
16041
|
-
const pkg = JSON.parse(
|
|
16436
|
+
const pkg = JSON.parse(readFileSync17(pkgPath, "utf-8"));
|
|
16042
16437
|
const allDeps = {
|
|
16043
16438
|
...pkg.dependencies,
|
|
16044
16439
|
...pkg.devDependencies
|
|
@@ -16072,7 +16467,7 @@ function detectPython(projectRoot) {
|
|
|
16072
16467
|
alembicDir: null
|
|
16073
16468
|
};
|
|
16074
16469
|
const markers = ["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"];
|
|
16075
|
-
const hasMarker = markers.some((m3) =>
|
|
16470
|
+
const hasMarker = markers.some((m3) => existsSync21(resolve12(projectRoot, m3)));
|
|
16076
16471
|
if (!hasMarker) return result;
|
|
16077
16472
|
result.detected = true;
|
|
16078
16473
|
const depFiles = [
|
|
@@ -16082,35 +16477,35 @@ function detectPython(projectRoot) {
|
|
|
16082
16477
|
{ file: "Pipfile" }
|
|
16083
16478
|
];
|
|
16084
16479
|
for (const { file } of depFiles) {
|
|
16085
|
-
const filePath =
|
|
16086
|
-
if (
|
|
16480
|
+
const filePath = resolve12(projectRoot, file);
|
|
16481
|
+
if (existsSync21(filePath)) {
|
|
16087
16482
|
try {
|
|
16088
|
-
const content =
|
|
16483
|
+
const content = readFileSync17(filePath, "utf-8").toLowerCase();
|
|
16089
16484
|
if (content.includes("fastapi")) result.hasFastapi = true;
|
|
16090
16485
|
if (content.includes("sqlalchemy")) result.hasSqlalchemy = true;
|
|
16091
16486
|
} catch {
|
|
16092
16487
|
}
|
|
16093
16488
|
}
|
|
16094
16489
|
}
|
|
16095
|
-
if (
|
|
16490
|
+
if (existsSync21(resolve12(projectRoot, "alembic.ini"))) {
|
|
16096
16491
|
result.hasAlembic = true;
|
|
16097
|
-
if (
|
|
16492
|
+
if (existsSync21(resolve12(projectRoot, "alembic"))) {
|
|
16098
16493
|
result.alembicDir = "alembic";
|
|
16099
16494
|
}
|
|
16100
|
-
} else if (
|
|
16495
|
+
} else if (existsSync21(resolve12(projectRoot, "alembic"))) {
|
|
16101
16496
|
result.hasAlembic = true;
|
|
16102
16497
|
result.alembicDir = "alembic";
|
|
16103
16498
|
}
|
|
16104
16499
|
const candidateRoots = ["app", "src", "backend", "api"];
|
|
16105
16500
|
for (const candidate of candidateRoots) {
|
|
16106
|
-
const candidatePath =
|
|
16107
|
-
if (
|
|
16501
|
+
const candidatePath = resolve12(projectRoot, candidate);
|
|
16502
|
+
if (existsSync21(candidatePath) && existsSync21(resolve12(candidatePath, "__init__.py"))) {
|
|
16108
16503
|
result.root = candidate;
|
|
16109
16504
|
break;
|
|
16110
16505
|
}
|
|
16111
|
-
if (
|
|
16506
|
+
if (existsSync21(candidatePath)) {
|
|
16112
16507
|
try {
|
|
16113
|
-
const files =
|
|
16508
|
+
const files = readdirSync13(candidatePath);
|
|
16114
16509
|
if (files.some((f2) => f2.endsWith(".py"))) {
|
|
16115
16510
|
result.root = candidate;
|
|
16116
16511
|
break;
|
|
@@ -16128,8 +16523,8 @@ function generateConfig(projectRoot, framework) {
|
|
|
16128
16523
|
console.warn(
|
|
16129
16524
|
"[@massu/core] generateConfig() is deprecated since 1.2.1 \u2014 use buildConfigFromDetection instead. It cannot produce valid configs for monorepos."
|
|
16130
16525
|
);
|
|
16131
|
-
const configPath =
|
|
16132
|
-
if (
|
|
16526
|
+
const configPath = resolve12(projectRoot, "massu.config.yaml");
|
|
16527
|
+
if (existsSync21(configPath)) {
|
|
16133
16528
|
return false;
|
|
16134
16529
|
}
|
|
16135
16530
|
const projectName = basename6(projectRoot);
|
|
@@ -16240,10 +16635,10 @@ function buildConfigFromDetection(opts) {
|
|
|
16240
16635
|
pathsSource = monorepoCommonRoot(detection.monorepo.packages);
|
|
16241
16636
|
}
|
|
16242
16637
|
}
|
|
16243
|
-
if (pathsSource === "src" && !
|
|
16638
|
+
if (pathsSource === "src" && !existsSync21(resolve12(projectRoot, "src"))) {
|
|
16244
16639
|
const fallbacks = ["app", "pages", "."];
|
|
16245
16640
|
for (const fallback of fallbacks) {
|
|
16246
|
-
if (fallback === "." ||
|
|
16641
|
+
if (fallback === "." || existsSync21(resolve12(projectRoot, fallback))) {
|
|
16247
16642
|
pathsSource = fallback;
|
|
16248
16643
|
break;
|
|
16249
16644
|
}
|
|
@@ -16327,7 +16722,7 @@ function buildConfigFromDetection(opts) {
|
|
|
16327
16722
|
};
|
|
16328
16723
|
if (pyFw?.framework) pythonBlock.framework = pyFw.framework;
|
|
16329
16724
|
if (pyFw?.orm) pythonBlock.orm = pyFw.orm;
|
|
16330
|
-
if (
|
|
16725
|
+
if (existsSync21(resolve12(projectRoot, "alembic.ini")) || existsSync21(resolve12(projectRoot, "alembic"))) {
|
|
16331
16726
|
pythonBlock.alembic_dir = "alembic";
|
|
16332
16727
|
}
|
|
16333
16728
|
config.python = pythonBlock;
|
|
@@ -16351,13 +16746,24 @@ function applyVariantTemplate(config, templatesDir) {
|
|
|
16351
16746
|
}
|
|
16352
16747
|
}
|
|
16353
16748
|
if (templateId === null) return config;
|
|
16354
|
-
const templatePath =
|
|
16355
|
-
if (!
|
|
16749
|
+
const templatePath = resolve12(templatesDir, templateId, "massu.config.yaml");
|
|
16750
|
+
if (!existsSync21(templatePath)) return config;
|
|
16356
16751
|
let template;
|
|
16357
16752
|
try {
|
|
16358
|
-
template = yamlParse(
|
|
16359
|
-
} catch {
|
|
16360
|
-
|
|
16753
|
+
template = yamlParse(readFileSync17(templatePath, "utf-8"));
|
|
16754
|
+
} catch (err) {
|
|
16755
|
+
throw new Error(
|
|
16756
|
+
`massu: the shipped stack template '${templateId}' is unreadable at ${templatePath}: ${err instanceof Error ? err.message : String(err)}
|
|
16757
|
+
This is a defect in @massu/core, not in your project. Please report it.`
|
|
16758
|
+
);
|
|
16759
|
+
}
|
|
16760
|
+
const validation = validateShippedTemplate(template, templateId);
|
|
16761
|
+
if (!validation.ok) {
|
|
16762
|
+
throw new Error(
|
|
16763
|
+
`massu: the shipped stack template '${templateId}' does not satisfy the template contract:
|
|
16764
|
+
${validation.issues.map((i) => ` - ${i}`).join("\n")}
|
|
16765
|
+
This is a defect in @massu/core, not in your project. Please report it.`
|
|
16766
|
+
);
|
|
16361
16767
|
}
|
|
16362
16768
|
const out = { ...config };
|
|
16363
16769
|
const tplFw = template.framework;
|
|
@@ -16414,9 +16820,9 @@ ${yamlStringify(config)}`;
|
|
|
16414
16820
|
function writeConfigAtomic(configPath, content) {
|
|
16415
16821
|
const tmpPath = `${configPath}.tmp`;
|
|
16416
16822
|
let existingMode;
|
|
16417
|
-
if (
|
|
16823
|
+
if (existsSync21(configPath)) {
|
|
16418
16824
|
try {
|
|
16419
|
-
existingMode =
|
|
16825
|
+
existingMode = statSync11(configPath).mode;
|
|
16420
16826
|
} catch {
|
|
16421
16827
|
existingMode = void 0;
|
|
16422
16828
|
}
|
|
@@ -16444,7 +16850,7 @@ function writeConfigAtomic(configPath, content) {
|
|
|
16444
16850
|
}
|
|
16445
16851
|
return { validated: true };
|
|
16446
16852
|
} catch (err) {
|
|
16447
|
-
if (
|
|
16853
|
+
if (existsSync21(tmpPath)) {
|
|
16448
16854
|
try {
|
|
16449
16855
|
rmSync6(tmpPath, { force: true });
|
|
16450
16856
|
} catch {
|
|
@@ -16455,8 +16861,8 @@ function writeConfigAtomic(configPath, content) {
|
|
|
16455
16861
|
}
|
|
16456
16862
|
function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
16457
16863
|
try {
|
|
16458
|
-
if (!
|
|
16459
|
-
const content =
|
|
16864
|
+
if (!existsSync21(configPath)) return "Config file does not exist after write";
|
|
16865
|
+
const content = readFileSync17(configPath, "utf-8");
|
|
16460
16866
|
const parsed = yamlParse(content);
|
|
16461
16867
|
if (parsed === null || typeof parsed !== "object") {
|
|
16462
16868
|
return "Config is not a valid YAML object";
|
|
@@ -16476,8 +16882,8 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
16476
16882
|
if (checkPaths) {
|
|
16477
16883
|
const src = cfg.paths.source;
|
|
16478
16884
|
if (src && src !== ".") {
|
|
16479
|
-
const srcAbs =
|
|
16480
|
-
if (!
|
|
16885
|
+
const srcAbs = resolve12(projectRoot, src);
|
|
16886
|
+
if (!existsSync21(srcAbs)) {
|
|
16481
16887
|
return `paths.source '${src}' does not exist on disk`;
|
|
16482
16888
|
}
|
|
16483
16889
|
}
|
|
@@ -16487,8 +16893,8 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
16487
16893
|
if (!Array.isArray(rawDirs)) continue;
|
|
16488
16894
|
for (const d2 of rawDirs) {
|
|
16489
16895
|
if (typeof d2 !== "string" || d2 === ".") continue;
|
|
16490
|
-
const abs =
|
|
16491
|
-
if (!
|
|
16896
|
+
const abs = resolve12(projectRoot, d2);
|
|
16897
|
+
if (!existsSync21(abs)) {
|
|
16492
16898
|
return `framework.languages.${lang}.source_dirs '${d2}' does not exist on disk`;
|
|
16493
16899
|
}
|
|
16494
16900
|
}
|
|
@@ -16497,7 +16903,7 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
16497
16903
|
if (Array.isArray(mRoots)) {
|
|
16498
16904
|
for (const r2 of mRoots) {
|
|
16499
16905
|
if (typeof r2 !== "string" || r2 === ".") continue;
|
|
16500
|
-
if (!
|
|
16906
|
+
if (!existsSync21(resolve12(projectRoot, r2))) {
|
|
16501
16907
|
return `paths.monorepo_roots '${r2}' does not exist on disk`;
|
|
16502
16908
|
}
|
|
16503
16909
|
}
|
|
@@ -16529,21 +16935,21 @@ function resolveTemplatesDir() {
|
|
|
16529
16935
|
const cwd = process.cwd();
|
|
16530
16936
|
const candidates = [
|
|
16531
16937
|
// Project-local install: `<project>/node_modules/@massu/core/templates`.
|
|
16532
|
-
|
|
16938
|
+
resolve12(cwd, "node_modules/@massu/core/templates"),
|
|
16533
16939
|
// Bundled cli.js layout: cli.js sits at `<package>/dist/cli.js`, so
|
|
16534
16940
|
// templates live one level up at `<package>/templates`. (Plan 1.5.1
|
|
16535
16941
|
// bug discovery: pre-existing layout assumed `dist/commands/init.js`
|
|
16536
16942
|
// depth which never matched the bundled cli, so resolveTemplatesDir
|
|
16537
16943
|
// returned null in production for both `--template` mode AND the
|
|
16538
16944
|
// applyVariantTemplate path.)
|
|
16539
|
-
|
|
16945
|
+
resolve12(__dirname2, "../templates"),
|
|
16540
16946
|
// Legacy nested layouts retained as fallbacks (in case a future
|
|
16541
16947
|
// build moves cli.js back into a subdirectory).
|
|
16542
|
-
|
|
16543
|
-
|
|
16948
|
+
resolve12(__dirname2, "../../templates"),
|
|
16949
|
+
resolve12(__dirname2, "../../../templates")
|
|
16544
16950
|
];
|
|
16545
16951
|
for (const c2 of candidates) {
|
|
16546
|
-
if (
|
|
16952
|
+
if (existsSync21(c2)) return c2;
|
|
16547
16953
|
}
|
|
16548
16954
|
return null;
|
|
16549
16955
|
}
|
|
@@ -16552,12 +16958,12 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
|
|
|
16552
16958
|
if (!templatesDir) {
|
|
16553
16959
|
return { success: false, error: `Templates directory not found (looked in node_modules and dist/src)` };
|
|
16554
16960
|
}
|
|
16555
|
-
const srcPath =
|
|
16556
|
-
if (!
|
|
16961
|
+
const srcPath = resolve12(templatesDir, templateName, "massu.config.yaml");
|
|
16962
|
+
if (!existsSync21(srcPath)) {
|
|
16557
16963
|
return { success: false, error: `Template '${templateName}' not found at ${srcPath}` };
|
|
16558
16964
|
}
|
|
16559
16965
|
try {
|
|
16560
|
-
let content =
|
|
16966
|
+
let content = readFileSync17(srcPath, "utf-8");
|
|
16561
16967
|
content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
|
|
16562
16968
|
writeFileSync5(targetPath, content, "utf-8");
|
|
16563
16969
|
return { success: true };
|
|
@@ -16567,13 +16973,13 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
|
|
|
16567
16973
|
}
|
|
16568
16974
|
function getInstallerVersion() {
|
|
16569
16975
|
const candidates = [
|
|
16570
|
-
|
|
16571
|
-
|
|
16976
|
+
resolve12(__dirname2, "../package.json"),
|
|
16977
|
+
resolve12(__dirname2, "../../package.json")
|
|
16572
16978
|
];
|
|
16573
16979
|
for (const candidate of candidates) {
|
|
16574
|
-
if (
|
|
16980
|
+
if (existsSync21(candidate)) {
|
|
16575
16981
|
try {
|
|
16576
|
-
const pkg = JSON.parse(
|
|
16982
|
+
const pkg = JSON.parse(readFileSync17(candidate, "utf-8"));
|
|
16577
16983
|
if (typeof pkg.version === "string" && pkg.version.length > 0 && pkg.name === "@massu/core") {
|
|
16578
16984
|
return pkg.version;
|
|
16579
16985
|
}
|
|
@@ -16586,11 +16992,11 @@ function getInstallerVersion() {
|
|
|
16586
16992
|
);
|
|
16587
16993
|
}
|
|
16588
16994
|
function registerMcpServer(projectRoot) {
|
|
16589
|
-
const mcpPath =
|
|
16995
|
+
const mcpPath = resolve12(projectRoot, ".mcp.json");
|
|
16590
16996
|
let existing = {};
|
|
16591
|
-
if (
|
|
16997
|
+
if (existsSync21(mcpPath)) {
|
|
16592
16998
|
try {
|
|
16593
|
-
existing = JSON.parse(
|
|
16999
|
+
existing = JSON.parse(readFileSync17(mcpPath, "utf-8"));
|
|
16594
17000
|
} catch {
|
|
16595
17001
|
existing = {};
|
|
16596
17002
|
}
|
|
@@ -16608,26 +17014,26 @@ function registerMcpServer(projectRoot) {
|
|
|
16608
17014
|
}
|
|
16609
17015
|
function resolveHooksDir() {
|
|
16610
17016
|
const cwd = process.cwd();
|
|
16611
|
-
const nodeModulesPath =
|
|
16612
|
-
if (
|
|
17017
|
+
const nodeModulesPath = resolve12(cwd, "node_modules/@massu/core/dist/hooks");
|
|
17018
|
+
if (existsSync21(nodeModulesPath)) {
|
|
16613
17019
|
return "node_modules/@massu/core/dist/hooks";
|
|
16614
17020
|
}
|
|
16615
|
-
const localPath =
|
|
16616
|
-
if (
|
|
17021
|
+
const localPath = resolve12(__dirname2, "../dist/hooks");
|
|
17022
|
+
if (existsSync21(localPath)) {
|
|
16617
17023
|
return localPath;
|
|
16618
17024
|
}
|
|
16619
17025
|
return "node_modules/@massu/core/dist/hooks";
|
|
16620
17026
|
}
|
|
16621
17027
|
function massuRuntimeDir(version, home = homedir7()) {
|
|
16622
|
-
return
|
|
17028
|
+
return resolve12(home, ".massu", "runtime", version);
|
|
16623
17029
|
}
|
|
16624
17030
|
function massuRuntimeCliPath(version, home = homedir7()) {
|
|
16625
|
-
return
|
|
17031
|
+
return resolve12(massuRuntimeDir(version, home), "node_modules", "@massu", "core", "dist", "cli.js");
|
|
16626
17032
|
}
|
|
16627
17033
|
function resolveMassuRuntimeCli(version, home = homedir7()) {
|
|
16628
17034
|
if (process.env.MASSU_NO_NODE_DIRECT === "1") return null;
|
|
16629
17035
|
const cli = massuRuntimeCliPath(version, home);
|
|
16630
|
-
if (!
|
|
17036
|
+
if (!existsSync21(cli)) return null;
|
|
16631
17037
|
try {
|
|
16632
17038
|
execFileSync2(process.execPath, [cli, "--version"], { stdio: "ignore", timeout: 2e4 });
|
|
16633
17039
|
return cli;
|
|
@@ -16652,7 +17058,7 @@ function materializeMassuRuntime(version, home = homedir7()) {
|
|
|
16652
17058
|
return resolveMassuRuntimeCli(version, home) !== null;
|
|
16653
17059
|
}
|
|
16654
17060
|
function massuShimPath(home = homedir7()) {
|
|
16655
|
-
return
|
|
17061
|
+
return resolve12(home, ".massu", "bin", "massu-hook");
|
|
16656
17062
|
}
|
|
16657
17063
|
function massuShimBody() {
|
|
16658
17064
|
return `#!/bin/sh
|
|
@@ -16700,7 +17106,7 @@ function resolveMassuShim(home = homedir7()) {
|
|
|
16700
17106
|
if (process.env.MASSU_NO_NODE_DIRECT === "1") return null;
|
|
16701
17107
|
if (process.platform === "win32") return null;
|
|
16702
17108
|
const shim = massuShimPath(home);
|
|
16703
|
-
if (!
|
|
17109
|
+
if (!existsSync21(shim)) return null;
|
|
16704
17110
|
try {
|
|
16705
17111
|
execFileSync2(shim, [], { stdio: "ignore", timeout: 1e4 });
|
|
16706
17112
|
return null;
|
|
@@ -16863,8 +17269,8 @@ function installHooks(projectRoot) {
|
|
|
16863
17269
|
} catch {
|
|
16864
17270
|
claudeDirName = ".claude";
|
|
16865
17271
|
}
|
|
16866
|
-
const claudeDir =
|
|
16867
|
-
if (!
|
|
17272
|
+
const claudeDir = resolve12(projectRoot, claudeDirName);
|
|
17273
|
+
if (!existsSync21(claudeDir)) {
|
|
16868
17274
|
mkdirSync10(claudeDir, { recursive: true });
|
|
16869
17275
|
}
|
|
16870
17276
|
const settings = readSettingsLocal(claudeDir);
|
|
@@ -16883,16 +17289,16 @@ function installHooks(projectRoot) {
|
|
|
16883
17289
|
}
|
|
16884
17290
|
function initMemoryDir(projectRoot) {
|
|
16885
17291
|
const encodedRoot = encodeMemoryDirName(projectRoot);
|
|
16886
|
-
const memoryDir =
|
|
17292
|
+
const memoryDir = resolve12(homedir7(), `.claude/projects/${encodedRoot}/memory`);
|
|
16887
17293
|
let migratedFromLegacy = false;
|
|
16888
|
-
const legacyDir =
|
|
16889
|
-
if (
|
|
17294
|
+
const legacyDir = resolve12(homedir7(), `.claude/projects/-${encodedRoot}/memory`);
|
|
17295
|
+
if (existsSync21(legacyDir) && !existsSync21(memoryDir)) {
|
|
16890
17296
|
try {
|
|
16891
|
-
mkdirSync10(
|
|
17297
|
+
mkdirSync10(resolve12(memoryDir, ".."), { recursive: true });
|
|
16892
17298
|
renameSync4(legacyDir, memoryDir);
|
|
16893
17299
|
try {
|
|
16894
|
-
const legacyParent =
|
|
16895
|
-
if (
|
|
17300
|
+
const legacyParent = resolve12(legacyDir, "..");
|
|
17301
|
+
if (existsSync21(legacyParent) && readdirSync13(legacyParent).length === 0) {
|
|
16896
17302
|
rmSync6(legacyParent, { recursive: false });
|
|
16897
17303
|
}
|
|
16898
17304
|
} catch {
|
|
@@ -16902,13 +17308,13 @@ function initMemoryDir(projectRoot) {
|
|
|
16902
17308
|
}
|
|
16903
17309
|
}
|
|
16904
17310
|
let created = false;
|
|
16905
|
-
if (!
|
|
17311
|
+
if (!existsSync21(memoryDir)) {
|
|
16906
17312
|
mkdirSync10(memoryDir, { recursive: true });
|
|
16907
17313
|
created = true;
|
|
16908
17314
|
}
|
|
16909
|
-
const memoryMdPath =
|
|
17315
|
+
const memoryMdPath = resolve12(memoryDir, "MEMORY.md");
|
|
16910
17316
|
let memoryMdCreated = false;
|
|
16911
|
-
if (!
|
|
17317
|
+
if (!existsSync21(memoryMdPath)) {
|
|
16912
17318
|
const projectName = basename6(projectRoot);
|
|
16913
17319
|
const memoryContent = `# ${projectName} - Massu Memory
|
|
16914
17320
|
|
|
@@ -17022,8 +17428,8 @@ async function runInit(argv, overrides) {
|
|
|
17022
17428
|
log("Massu AI - Project Setup");
|
|
17023
17429
|
log("========================");
|
|
17024
17430
|
log("");
|
|
17025
|
-
const configPath =
|
|
17026
|
-
if (
|
|
17431
|
+
const configPath = resolve12(projectRoot, "massu.config.yaml");
|
|
17432
|
+
if (existsSync21(configPath)) {
|
|
17027
17433
|
if (opts.ci && !opts.force) {
|
|
17028
17434
|
errLog(`error: massu.config.yaml already exists at ${configPath}`);
|
|
17029
17435
|
errLog(" rerun with --force to overwrite, or remove the file first");
|
|
@@ -17162,8 +17568,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
17162
17568
|
const commandStats = cmdResult.assets.commands;
|
|
17163
17569
|
const stackResolved = !emptyStack && commandStats && (commandStats.installed > 0 || commandStats.updated > 0 || commandStats.kept > 0);
|
|
17164
17570
|
if (!stackResolved) {
|
|
17165
|
-
const placeholderPath =
|
|
17166
|
-
if (!
|
|
17571
|
+
const placeholderPath = resolve12(cmdResult.claudeDir, "commands", "_massu-needs-stack.md");
|
|
17572
|
+
if (!existsSync21(placeholderPath)) {
|
|
17167
17573
|
const placeholderBody = [
|
|
17168
17574
|
"# Massu \u2014 stack not yet detected",
|
|
17169
17575
|
"",
|
|
@@ -17184,7 +17590,7 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
17184
17590
|
"\u2014 Massu"
|
|
17185
17591
|
].join("\n");
|
|
17186
17592
|
try {
|
|
17187
|
-
mkdirSync10(
|
|
17593
|
+
mkdirSync10(resolve12(cmdResult.claudeDir, "commands"), { recursive: true });
|
|
17188
17594
|
writeFileSync5(placeholderPath, placeholderBody, "utf-8");
|
|
17189
17595
|
log(" Wrote _massu-needs-stack.md placeholder (no stack detected yet)");
|
|
17190
17596
|
} catch {
|
|
@@ -17207,8 +17613,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
17207
17613
|
(async () => {
|
|
17208
17614
|
try {
|
|
17209
17615
|
const encodedRoot = encodeMemoryDirName(projectRoot);
|
|
17210
|
-
const memoryDir =
|
|
17211
|
-
const memFiles =
|
|
17616
|
+
const memoryDir = resolve12(homedir7(), ".claude", "projects", encodedRoot, "memory");
|
|
17617
|
+
const memFiles = existsSync21(memoryDir) ? readdirSync13(memoryDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md") : [];
|
|
17212
17618
|
if (memFiles.length > 0) {
|
|
17213
17619
|
const { getMemoryDb: getMemoryDb2 } = await Promise.resolve().then(() => (init_memory_db(), memory_db_exports));
|
|
17214
17620
|
const db = getMemoryDb2();
|
|
@@ -17264,6 +17670,7 @@ var init_init = __esm({
|
|
|
17264
17670
|
"use strict";
|
|
17265
17671
|
init_memory_file_ingest();
|
|
17266
17672
|
init_config();
|
|
17673
|
+
init_shipped_template_schema();
|
|
17267
17674
|
init_install_commands();
|
|
17268
17675
|
init_settings_local();
|
|
17269
17676
|
init_memory_path();
|
|
@@ -17890,10 +18297,10 @@ var init_hook_registry = __esm({
|
|
|
17890
18297
|
});
|
|
17891
18298
|
|
|
17892
18299
|
// src/preflight.ts
|
|
17893
|
-
import { existsSync as
|
|
18300
|
+
import { existsSync as existsSync22 } from "fs";
|
|
17894
18301
|
function assertCodegraphUsable(dbPath) {
|
|
17895
18302
|
const path = dbPath ?? getResolvedPaths().codegraphDbPath;
|
|
17896
|
-
if (!
|
|
18303
|
+
if (!existsSync22(path)) {
|
|
17897
18304
|
throw new CodegraphDbUnusableError(path, "missing");
|
|
17898
18305
|
}
|
|
17899
18306
|
let db = null;
|
|
@@ -17978,8 +18385,8 @@ __export(hook_runner_exports, {
|
|
|
17978
18385
|
resolveHookFile: () => resolveHookFile,
|
|
17979
18386
|
runHookRunner: () => runHookRunner
|
|
17980
18387
|
});
|
|
17981
|
-
import { existsSync as
|
|
17982
|
-
import { resolve as
|
|
18388
|
+
import { existsSync as existsSync23 } from "fs";
|
|
18389
|
+
import { resolve as resolve13, dirname as dirname13 } from "path";
|
|
17983
18390
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
17984
18391
|
import { spawn } from "child_process";
|
|
17985
18392
|
function resolveHookFile(hookName) {
|
|
@@ -17991,14 +18398,14 @@ function resolveHookFile(hookName) {
|
|
|
17991
18398
|
}
|
|
17992
18399
|
const candidates = [
|
|
17993
18400
|
// Bundled compiled layout: dist/cli.js → ./hooks/<file>.js
|
|
17994
|
-
|
|
18401
|
+
resolve13(__dirname3, "hooks", file),
|
|
17995
18402
|
// TS-source dev / sibling layout: src/commands/ → ../hooks/<file>
|
|
17996
|
-
|
|
18403
|
+
resolve13(__dirname3, "../hooks", file),
|
|
17997
18404
|
// TS-source dev fallback: src/commands/ → ../../dist/hooks/<file>
|
|
17998
|
-
|
|
18405
|
+
resolve13(__dirname3, "../../dist/hooks", file)
|
|
17999
18406
|
];
|
|
18000
18407
|
for (const candidate of candidates) {
|
|
18001
|
-
if (
|
|
18408
|
+
if (existsSync23(candidate)) {
|
|
18002
18409
|
return candidate;
|
|
18003
18410
|
}
|
|
18004
18411
|
}
|
|
@@ -18071,17 +18478,17 @@ __export(doctor_exports, {
|
|
|
18071
18478
|
runDoctor: () => runDoctor,
|
|
18072
18479
|
runValidateConfig: () => runValidateConfig
|
|
18073
18480
|
});
|
|
18074
|
-
import { existsSync as
|
|
18075
|
-
import { resolve as
|
|
18481
|
+
import { existsSync as existsSync24, readFileSync as readFileSync18, readdirSync as readdirSync14 } from "fs";
|
|
18482
|
+
import { resolve as resolve14, dirname as dirname14 } from "path";
|
|
18076
18483
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
18077
18484
|
import { parse as parseYaml4 } from "yaml";
|
|
18078
18485
|
function checkConfig(projectRoot) {
|
|
18079
|
-
const configPath =
|
|
18080
|
-
if (!
|
|
18486
|
+
const configPath = resolve14(projectRoot, "massu.config.yaml");
|
|
18487
|
+
if (!existsSync24(configPath)) {
|
|
18081
18488
|
return { name: "Configuration", status: "fail", detail: "massu.config.yaml not found. Run: npx massu init" };
|
|
18082
18489
|
}
|
|
18083
18490
|
try {
|
|
18084
|
-
const content =
|
|
18491
|
+
const content = readFileSync18(configPath, "utf-8");
|
|
18085
18492
|
const parsed = parseYaml4(content);
|
|
18086
18493
|
if (!parsed || typeof parsed !== "object") {
|
|
18087
18494
|
return { name: "Configuration", status: "fail", detail: "massu.config.yaml is empty or invalid YAML" };
|
|
@@ -18093,11 +18500,11 @@ function checkConfig(projectRoot) {
|
|
|
18093
18500
|
}
|
|
18094
18501
|
function checkMcpServer(projectRoot) {
|
|
18095
18502
|
const mcpPath = getResolvedPaths().mcpJsonPath;
|
|
18096
|
-
if (!
|
|
18503
|
+
if (!existsSync24(mcpPath)) {
|
|
18097
18504
|
return { name: "MCP Server", status: "fail", detail: ".mcp.json not found. Run: npx massu init" };
|
|
18098
18505
|
}
|
|
18099
18506
|
try {
|
|
18100
|
-
const content = JSON.parse(
|
|
18507
|
+
const content = JSON.parse(readFileSync18(mcpPath, "utf-8"));
|
|
18101
18508
|
const servers = content.mcpServers ?? {};
|
|
18102
18509
|
if (!servers.massu) {
|
|
18103
18510
|
return { name: "MCP Server", status: "fail", detail: "massu not registered in .mcp.json. Run: npx massu init" };
|
|
@@ -18111,7 +18518,7 @@ function readHookSources() {
|
|
|
18111
18518
|
const { settingsPath, settingsLocalPath } = getResolvedPaths();
|
|
18112
18519
|
const out = [];
|
|
18113
18520
|
for (const [label, p19] of [["settings.json", settingsPath], ["settings.local.json", settingsLocalPath]]) {
|
|
18114
|
-
if (!
|
|
18521
|
+
if (!existsSync24(p19)) continue;
|
|
18115
18522
|
const content = readSettingsAtPath(p19);
|
|
18116
18523
|
out.push({ label, hooks: content.hooks });
|
|
18117
18524
|
}
|
|
@@ -18150,11 +18557,11 @@ function checkHooksConfig(_projectRoot2) {
|
|
|
18150
18557
|
}
|
|
18151
18558
|
}
|
|
18152
18559
|
function checkHookFiles(projectRoot) {
|
|
18153
|
-
const nodeModulesHooksDir =
|
|
18560
|
+
const nodeModulesHooksDir = resolve14(projectRoot, "node_modules/@massu/core/dist/hooks");
|
|
18154
18561
|
let hooksDir = nodeModulesHooksDir;
|
|
18155
|
-
if (!
|
|
18156
|
-
const devHooksDir =
|
|
18157
|
-
if (
|
|
18562
|
+
if (!existsSync24(nodeModulesHooksDir)) {
|
|
18563
|
+
const devHooksDir = resolve14(__dirname4, "../../dist/hooks");
|
|
18564
|
+
if (existsSync24(devHooksDir)) {
|
|
18158
18565
|
hooksDir = devHooksDir;
|
|
18159
18566
|
} else {
|
|
18160
18567
|
return { name: "Hook Files", status: "fail", detail: "Compiled hooks not found. Run: npm install @massu/core" };
|
|
@@ -18162,7 +18569,7 @@ function checkHookFiles(projectRoot) {
|
|
|
18162
18569
|
}
|
|
18163
18570
|
const missing = [];
|
|
18164
18571
|
for (const hookFile of EXPECTED_HOOKS) {
|
|
18165
|
-
if (!
|
|
18572
|
+
if (!existsSync24(resolve14(hooksDir, hookFile))) {
|
|
18166
18573
|
missing.push(hookFile);
|
|
18167
18574
|
}
|
|
18168
18575
|
}
|
|
@@ -18188,12 +18595,12 @@ async function checkHookExecution(projectRoot, opts = {}) {
|
|
|
18188
18595
|
detail: `Canary hook file unresolved: ${err instanceof Error ? err.message : String(err)}. Re-run: npx -y @massu/core init`
|
|
18189
18596
|
};
|
|
18190
18597
|
}
|
|
18191
|
-
const tmpCwd = mkdtempSync2(
|
|
18598
|
+
const tmpCwd = mkdtempSync2(resolve14(tmpdir2(), "massu-hook-canary-"));
|
|
18192
18599
|
try {
|
|
18193
|
-
const realConfig =
|
|
18194
|
-
if (
|
|
18600
|
+
const realConfig = resolve14(projectRoot, "massu.config.yaml");
|
|
18601
|
+
if (existsSync24(realConfig)) {
|
|
18195
18602
|
try {
|
|
18196
|
-
copyFileSync4(realConfig,
|
|
18603
|
+
copyFileSync4(realConfig, resolve14(tmpCwd, "massu.config.yaml"));
|
|
18197
18604
|
} catch {
|
|
18198
18605
|
}
|
|
18199
18606
|
}
|
|
@@ -18256,8 +18663,8 @@ function checkNodeVersion2() {
|
|
|
18256
18663
|
return { name: "Node.js", status: "fail", detail: `v${version} \u2014 Node.js >= ${floor} is required (node:sqlite engine)` };
|
|
18257
18664
|
}
|
|
18258
18665
|
async function checkGitRepo(projectRoot) {
|
|
18259
|
-
const gitDir =
|
|
18260
|
-
if (!
|
|
18666
|
+
const gitDir = resolve14(projectRoot, ".git");
|
|
18667
|
+
if (!existsSync24(gitDir)) {
|
|
18261
18668
|
return { name: "Git Repository", status: "warn", detail: "Not a git repository (optional but recommended)" };
|
|
18262
18669
|
}
|
|
18263
18670
|
try {
|
|
@@ -18275,7 +18682,7 @@ async function checkGitRepo(projectRoot) {
|
|
|
18275
18682
|
}
|
|
18276
18683
|
function checkKnowledgeDb(projectRoot) {
|
|
18277
18684
|
const knowledgeDbPath = getResolvedPaths().memoryDbPath;
|
|
18278
|
-
if (!
|
|
18685
|
+
if (!existsSync24(knowledgeDbPath)) {
|
|
18279
18686
|
return {
|
|
18280
18687
|
name: "Knowledge DB",
|
|
18281
18688
|
status: "warn",
|
|
@@ -18286,7 +18693,7 @@ function checkKnowledgeDb(projectRoot) {
|
|
|
18286
18693
|
}
|
|
18287
18694
|
function checkMemoryDir(_projectRoot2) {
|
|
18288
18695
|
const memoryDir = getResolvedPaths().memoryDir;
|
|
18289
|
-
if (!
|
|
18696
|
+
if (!existsSync24(memoryDir)) {
|
|
18290
18697
|
return {
|
|
18291
18698
|
name: "Memory Directory",
|
|
18292
18699
|
status: "warn",
|
|
@@ -18396,8 +18803,8 @@ async function checkApiKeyHygiene(projectRoot) {
|
|
|
18396
18803
|
function checkPythonHealth(projectRoot) {
|
|
18397
18804
|
const config = getConfig();
|
|
18398
18805
|
if (!config.python?.root) return null;
|
|
18399
|
-
const pythonRoot =
|
|
18400
|
-
if (!
|
|
18806
|
+
const pythonRoot = resolve14(projectRoot, config.python.root);
|
|
18807
|
+
if (!existsSync24(pythonRoot)) {
|
|
18401
18808
|
return {
|
|
18402
18809
|
name: "Python",
|
|
18403
18810
|
status: "fail",
|
|
@@ -18411,15 +18818,15 @@ function checkPythonHealth(projectRoot) {
|
|
|
18411
18818
|
function scanDir(dir, depth) {
|
|
18412
18819
|
if (depth > 5) return;
|
|
18413
18820
|
try {
|
|
18414
|
-
const entries =
|
|
18821
|
+
const entries = readdirSync14(dir, { withFileTypes: true });
|
|
18415
18822
|
for (const entry of entries) {
|
|
18416
18823
|
if (entry.isDirectory()) {
|
|
18417
18824
|
const excludeDirs = config.python?.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
|
|
18418
18825
|
if (!excludeDirs.includes(entry.name)) {
|
|
18419
|
-
const subdir =
|
|
18420
|
-
if (depth <= 2 && !
|
|
18826
|
+
const subdir = resolve14(dir, entry.name);
|
|
18827
|
+
if (depth <= 2 && !existsSync24(resolve14(subdir, "__init__.py"))) {
|
|
18421
18828
|
try {
|
|
18422
|
-
const subEntries =
|
|
18829
|
+
const subEntries = readdirSync14(subdir);
|
|
18423
18830
|
if (subEntries.some((f2) => f2.endsWith(".py") && f2 !== "__init__.py")) {
|
|
18424
18831
|
initPyMissing.push(entry.name);
|
|
18425
18832
|
}
|
|
@@ -18515,15 +18922,15 @@ async function runDoctor() {
|
|
|
18515
18922
|
}
|
|
18516
18923
|
async function runValidateConfig() {
|
|
18517
18924
|
const projectRoot = process.cwd();
|
|
18518
|
-
const configPath =
|
|
18519
|
-
if (!
|
|
18925
|
+
const configPath = resolve14(projectRoot, "massu.config.yaml");
|
|
18926
|
+
if (!existsSync24(configPath)) {
|
|
18520
18927
|
console.error("Error: massu.config.yaml not found in current directory");
|
|
18521
18928
|
console.error("Run: npx massu init");
|
|
18522
18929
|
process.exit(1);
|
|
18523
18930
|
return;
|
|
18524
18931
|
}
|
|
18525
18932
|
try {
|
|
18526
|
-
const content =
|
|
18933
|
+
const content = readFileSync18(configPath, "utf-8");
|
|
18527
18934
|
const parsed = parseYaml4(content);
|
|
18528
18935
|
if (!parsed || typeof parsed !== "object") {
|
|
18529
18936
|
console.error("Error: massu.config.yaml is empty or not a valid YAML object");
|
|
@@ -18873,7 +19280,7 @@ __export(permissions_exports, {
|
|
|
18873
19280
|
handlePermissionsSubcommand: () => handlePermissionsSubcommand,
|
|
18874
19281
|
printPermissionsHelp: () => printPermissionsHelp
|
|
18875
19282
|
});
|
|
18876
|
-
import { resolve as
|
|
19283
|
+
import { resolve as resolve15 } from "path";
|
|
18877
19284
|
function resolveClaudeDir2() {
|
|
18878
19285
|
let claudeDirName = ".claude";
|
|
18879
19286
|
try {
|
|
@@ -18881,7 +19288,7 @@ function resolveClaudeDir2() {
|
|
|
18881
19288
|
} catch {
|
|
18882
19289
|
claudeDirName = ".claude";
|
|
18883
19290
|
}
|
|
18884
|
-
return
|
|
19291
|
+
return resolve15(process.cwd(), claudeDirName);
|
|
18885
19292
|
}
|
|
18886
19293
|
async function handlePermissionsSubcommand(args2) {
|
|
18887
19294
|
const sub = args2[0];
|
|
@@ -18999,7 +19406,7 @@ var db_exports = {};
|
|
|
18999
19406
|
__export(db_exports, {
|
|
19000
19407
|
handleDbSubcommand: () => handleDbSubcommand
|
|
19001
19408
|
});
|
|
19002
|
-
import { existsSync as
|
|
19409
|
+
import { existsSync as existsSync25, statSync as statSync12 } from "fs";
|
|
19003
19410
|
import { basename as basename7 } from "path";
|
|
19004
19411
|
function human(bytes) {
|
|
19005
19412
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -19044,7 +19451,7 @@ async function handleDbSubcommand(args2) {
|
|
|
19044
19451
|
|
|
19045
19452
|
`);
|
|
19046
19453
|
for (const store of allStores()) {
|
|
19047
|
-
if (!
|
|
19454
|
+
if (!existsSync25(store.path)) {
|
|
19048
19455
|
process.stdout.write(` ${"\u2014"} ${store.name.padEnd(10)} not present, skipped
|
|
19049
19456
|
`);
|
|
19050
19457
|
continue;
|
|
@@ -19142,12 +19549,12 @@ Restore with: massu db restore --latest memory
|
|
|
19142
19549
|
target = found.path;
|
|
19143
19550
|
}
|
|
19144
19551
|
try {
|
|
19145
|
-
const before =
|
|
19552
|
+
const before = existsSync25(target) ? statSync12(target).size : 0;
|
|
19146
19553
|
const res = restoreDb(backupPath, target);
|
|
19147
19554
|
process.stdout.write(
|
|
19148
19555
|
`Restored ${basename7(target)}
|
|
19149
19556
|
from : ${res.restoredFrom}
|
|
19150
|
-
size : ${human(before)} -> ${human(
|
|
19557
|
+
size : ${human(before)} -> ${human(statSync12(target).size)}
|
|
19151
19558
|
` + (res.preservedOriginal ? ` the previous file was NOT destroyed \u2014 kept at:
|
|
19152
19559
|
${res.preservedOriginal}
|
|
19153
19560
|
` : "") + ` integrity_check: ok (verified before AND after the restore)
|
|
@@ -19308,8 +19715,8 @@ var init_license2 = __esm({
|
|
|
19308
19715
|
});
|
|
19309
19716
|
|
|
19310
19717
|
// src/changelog-generator.ts
|
|
19311
|
-
import { existsSync as
|
|
19312
|
-
import { resolve as
|
|
19718
|
+
import { existsSync as existsSync26, readFileSync as readFileSync19, readdirSync as readdirSync15 } from "fs";
|
|
19719
|
+
import { resolve as resolve16 } from "path";
|
|
19313
19720
|
function parseCommitsForPlanTokens(subjects) {
|
|
19314
19721
|
const tokens = /* @__PURE__ */ new Set();
|
|
19315
19722
|
const maintenance = [];
|
|
@@ -19326,16 +19733,16 @@ function parseCommitsForPlanTokens(subjects) {
|
|
|
19326
19733
|
function loadPlanSummaries(tokens, planDir) {
|
|
19327
19734
|
const result = /* @__PURE__ */ new Map();
|
|
19328
19735
|
if (tokens.size === 0) return result;
|
|
19329
|
-
if (!
|
|
19736
|
+
if (!existsSync26(planDir)) {
|
|
19330
19737
|
throw new Error(`Plan directory does not exist: ${planDir}`);
|
|
19331
19738
|
}
|
|
19332
|
-
const files =
|
|
19739
|
+
const files = readdirSync15(planDir).filter((f2) => f2.endsWith(".md"));
|
|
19333
19740
|
for (const token of tokens) {
|
|
19334
19741
|
let matchedFile = null;
|
|
19335
19742
|
let content = "";
|
|
19336
19743
|
for (const file of files) {
|
|
19337
|
-
const path =
|
|
19338
|
-
const text18 =
|
|
19744
|
+
const path = resolve16(planDir, file);
|
|
19745
|
+
const text18 = readFileSync19(path, "utf-8");
|
|
19339
19746
|
const tokenRe = new RegExp(
|
|
19340
19747
|
`^\\*\\*Plan Token\\*\\*:\\s*\`?${token.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}\`?(\\s|$)`,
|
|
19341
19748
|
"m"
|
|
@@ -19416,8 +19823,8 @@ __export(changelog_exports, {
|
|
|
19416
19823
|
printChangelogHelp: () => printChangelogHelp
|
|
19417
19824
|
});
|
|
19418
19825
|
import { execSync } from "child_process";
|
|
19419
|
-
import { existsSync as
|
|
19420
|
-
import { resolve as
|
|
19826
|
+
import { existsSync as existsSync27, readFileSync as readFileSync20 } from "fs";
|
|
19827
|
+
import { resolve as resolve17 } from "path";
|
|
19421
19828
|
function resolveRepoRoot() {
|
|
19422
19829
|
try {
|
|
19423
19830
|
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
@@ -19441,25 +19848,25 @@ function getCommitSubjects(range) {
|
|
|
19441
19848
|
}
|
|
19442
19849
|
}
|
|
19443
19850
|
function getCurrentVersion(repoRoot) {
|
|
19444
|
-
const pkgPath =
|
|
19445
|
-
if (!
|
|
19446
|
-
const pkg = JSON.parse(
|
|
19851
|
+
const pkgPath = resolve17(repoRoot, "packages/core/package.json");
|
|
19852
|
+
if (!existsSync27(pkgPath)) return "0.0.0";
|
|
19853
|
+
const pkg = JSON.parse(readFileSync20(pkgPath, "utf-8"));
|
|
19447
19854
|
return pkg.version || "0.0.0";
|
|
19448
19855
|
}
|
|
19449
19856
|
function todayDate() {
|
|
19450
19857
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
19451
19858
|
}
|
|
19452
19859
|
function getLatestChangelogEntryBody(repoRoot) {
|
|
19453
|
-
const path =
|
|
19454
|
-
if (!
|
|
19455
|
-
const content =
|
|
19860
|
+
const path = resolve17(repoRoot, "CHANGELOG.md");
|
|
19861
|
+
if (!existsSync27(path)) return "";
|
|
19862
|
+
const content = readFileSync20(path, "utf-8");
|
|
19456
19863
|
const m3 = content.match(/^## \[[\d.]+\][^\n]*\n([\s\S]*?)(?=\n## \[|$)/m);
|
|
19457
19864
|
return m3 ? m3[1] : "";
|
|
19458
19865
|
}
|
|
19459
19866
|
async function handleChangelogSubcommand(args2) {
|
|
19460
19867
|
const sub = args2[0];
|
|
19461
19868
|
const repoRoot = resolveRepoRoot();
|
|
19462
|
-
const planDir =
|
|
19869
|
+
const planDir = resolve17(repoRoot, "docs/plans");
|
|
19463
19870
|
switch (sub) {
|
|
19464
19871
|
case "generate": {
|
|
19465
19872
|
const lastTag = getLastTag();
|
|
@@ -19548,11 +19955,11 @@ var init_changelog = __esm({
|
|
|
19548
19955
|
|
|
19549
19956
|
// src/knowledge-db.ts
|
|
19550
19957
|
import { dirname as dirname15 } from "path";
|
|
19551
|
-
import { existsSync as
|
|
19958
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync11 } from "fs";
|
|
19552
19959
|
function getKnowledgeDb() {
|
|
19553
19960
|
const dbPath = getResolvedPaths().knowledgeDbPath;
|
|
19554
19961
|
const dir = dirname15(dbPath);
|
|
19555
|
-
if (!
|
|
19962
|
+
if (!existsSync28(dir)) {
|
|
19556
19963
|
mkdirSync11(dir, { recursive: true });
|
|
19557
19964
|
}
|
|
19558
19965
|
const db = openDatabase2(dbPath);
|
|
@@ -19779,7 +20186,7 @@ var memory_embed_backfill_exports = {};
|
|
|
19779
20186
|
__export(memory_embed_backfill_exports, {
|
|
19780
20187
|
runMemoryEmbedBackfill: () => runMemoryEmbedBackfill
|
|
19781
20188
|
});
|
|
19782
|
-
import { existsSync as
|
|
20189
|
+
import { existsSync as existsSync29 } from "fs";
|
|
19783
20190
|
async function runMemoryEmbedBackfill(_args = []) {
|
|
19784
20191
|
await embed("massu memory embed backfill probe");
|
|
19785
20192
|
const active = getActiveEmbedModel();
|
|
@@ -19806,7 +20213,7 @@ async function runMemoryEmbedBackfill(_args = []) {
|
|
|
19806
20213
|
);
|
|
19807
20214
|
let chunks = { embedded: 0, scanned: 0 };
|
|
19808
20215
|
const knowledgeDbPath = getResolvedPaths().knowledgeDbPath;
|
|
19809
|
-
if (
|
|
20216
|
+
if (existsSync29(knowledgeDbPath)) {
|
|
19810
20217
|
const knowledgeDb = getKnowledgeDb();
|
|
19811
20218
|
try {
|
|
19812
20219
|
chunks = await embedMissingChunks(knowledgeDb);
|
|
@@ -19839,17 +20246,17 @@ var init_memory_embed_backfill = __esm({
|
|
|
19839
20246
|
|
|
19840
20247
|
// src/memory-authorship.ts
|
|
19841
20248
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
19842
|
-
import { readFileSync as
|
|
20249
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync6, existsSync as existsSync30, mkdirSync as mkdirSync12, chmodSync as chmodSync6, statSync as statSync13 } from "fs";
|
|
19843
20250
|
import { homedir as homedir8 } from "os";
|
|
19844
|
-
import { resolve as
|
|
20251
|
+
import { resolve as resolve18 } from "path";
|
|
19845
20252
|
function renderKeyPath(home = homedir8()) {
|
|
19846
|
-
return
|
|
20253
|
+
return resolve18(home, ".massu", "render-key");
|
|
19847
20254
|
}
|
|
19848
20255
|
function readRenderKey(home = homedir8()) {
|
|
19849
20256
|
const p19 = renderKeyPath(home);
|
|
19850
20257
|
try {
|
|
19851
|
-
if (!
|
|
19852
|
-
const raw =
|
|
20258
|
+
if (!existsSync30(p19)) return void 0;
|
|
20259
|
+
const raw = readFileSync21(p19);
|
|
19853
20260
|
if (raw.length !== 32) return void 0;
|
|
19854
20261
|
return raw;
|
|
19855
20262
|
} catch {
|
|
@@ -19861,7 +20268,7 @@ function ensureRenderKey(home = homedir8()) {
|
|
|
19861
20268
|
if (existing) return existing;
|
|
19862
20269
|
const p19 = renderKeyPath(home);
|
|
19863
20270
|
try {
|
|
19864
|
-
const dir =
|
|
20271
|
+
const dir = resolve18(home, ".massu");
|
|
19865
20272
|
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
19866
20273
|
try {
|
|
19867
20274
|
chmodSync6(dir, 448);
|
|
@@ -19919,11 +20326,11 @@ var init_memory_authorship = __esm({
|
|
|
19919
20326
|
});
|
|
19920
20327
|
|
|
19921
20328
|
// src/rule-candidate-snapshot.ts
|
|
19922
|
-
import { existsSync as
|
|
20329
|
+
import { existsSync as existsSync31, readFileSync as readFileSync22, writeFileSync as writeFileSync7, unlinkSync as unlinkSync4 } from "fs";
|
|
19923
20330
|
function takeSnapshots(paths) {
|
|
19924
20331
|
const out = /* @__PURE__ */ new Map();
|
|
19925
20332
|
for (const p19 of paths) {
|
|
19926
|
-
if (
|
|
20333
|
+
if (existsSync31(p19)) out.set(p19, readFileSync22(p19, "utf-8"));
|
|
19927
20334
|
else out.set(p19, null);
|
|
19928
20335
|
}
|
|
19929
20336
|
return out;
|
|
@@ -19933,7 +20340,7 @@ function restoreSnapshots(snapshot) {
|
|
|
19933
20340
|
for (const [path, content] of snapshot) {
|
|
19934
20341
|
try {
|
|
19935
20342
|
if (content === null) {
|
|
19936
|
-
if (
|
|
20343
|
+
if (existsSync31(path)) unlinkSync4(path);
|
|
19937
20344
|
} else {
|
|
19938
20345
|
writeFileSync7(path, content, "utf-8");
|
|
19939
20346
|
}
|
|
@@ -20013,7 +20420,7 @@ var init_memory_render_path = __esm({
|
|
|
20013
20420
|
});
|
|
20014
20421
|
|
|
20015
20422
|
// src/memory-tombstones.ts
|
|
20016
|
-
import { readFileSync as
|
|
20423
|
+
import { readFileSync as readFileSync23, appendFileSync as appendFileSync3, existsSync as existsSync32 } from "fs";
|
|
20017
20424
|
import { join as join16 } from "path";
|
|
20018
20425
|
function tombstoneLedgerPath(memoryDir) {
|
|
20019
20426
|
return join16(memoryDir, TOMBSTONE_LEDGER);
|
|
@@ -20021,10 +20428,10 @@ function tombstoneLedgerPath(memoryDir) {
|
|
|
20021
20428
|
function readTombstones(memoryDir) {
|
|
20022
20429
|
const out = /* @__PURE__ */ new Map();
|
|
20023
20430
|
const p19 = tombstoneLedgerPath(memoryDir);
|
|
20024
|
-
if (!
|
|
20431
|
+
if (!existsSync32(p19)) return out;
|
|
20025
20432
|
let raw;
|
|
20026
20433
|
try {
|
|
20027
|
-
raw =
|
|
20434
|
+
raw = readFileSync23(p19, "utf8");
|
|
20028
20435
|
} catch {
|
|
20029
20436
|
return out;
|
|
20030
20437
|
}
|
|
@@ -20263,29 +20670,29 @@ var init_memory_llm = __esm({
|
|
|
20263
20670
|
|
|
20264
20671
|
// src/memory-backup.ts
|
|
20265
20672
|
import {
|
|
20266
|
-
readdirSync as
|
|
20673
|
+
readdirSync as readdirSync16,
|
|
20267
20674
|
mkdirSync as mkdirSync13,
|
|
20268
20675
|
copyFileSync as copyFileSync2,
|
|
20269
|
-
statSync as
|
|
20270
|
-
existsSync as
|
|
20676
|
+
statSync as statSync14,
|
|
20677
|
+
existsSync as existsSync33,
|
|
20271
20678
|
rmSync as rmSync7,
|
|
20272
|
-
readFileSync as
|
|
20679
|
+
readFileSync as readFileSync24
|
|
20273
20680
|
} from "fs";
|
|
20274
20681
|
import { homedir as homedir9 } from "os";
|
|
20275
|
-
import { join as join17, resolve as
|
|
20682
|
+
import { join as join17, resolve as resolve19 } from "path";
|
|
20276
20683
|
function backupsRoot(home = homedir9()) {
|
|
20277
|
-
return
|
|
20684
|
+
return resolve19(home, ".massu", "memory-backups");
|
|
20278
20685
|
}
|
|
20279
20686
|
function backupStamp2(nowMs) {
|
|
20280
20687
|
return new Date(nowMs).toISOString().replace(/:/g, "-");
|
|
20281
20688
|
}
|
|
20282
20689
|
function newestMtimeMs(dir) {
|
|
20283
|
-
if (!
|
|
20690
|
+
if (!existsSync33(dir)) return 0;
|
|
20284
20691
|
let newest = 0;
|
|
20285
|
-
for (const entry of
|
|
20692
|
+
for (const entry of readdirSync16(dir)) {
|
|
20286
20693
|
const p19 = join17(dir, entry);
|
|
20287
20694
|
try {
|
|
20288
|
-
const st =
|
|
20695
|
+
const st = statSync14(p19);
|
|
20289
20696
|
if (st.isFile() && st.mtimeMs > newest) newest = st.mtimeMs;
|
|
20290
20697
|
} catch {
|
|
20291
20698
|
continue;
|
|
@@ -20295,12 +20702,12 @@ function newestMtimeMs(dir) {
|
|
|
20295
20702
|
}
|
|
20296
20703
|
function listBackups(home = homedir9()) {
|
|
20297
20704
|
const root = backupsRoot(home);
|
|
20298
|
-
if (!
|
|
20705
|
+
if (!existsSync33(root)) return [];
|
|
20299
20706
|
const out = [];
|
|
20300
|
-
for (const stamp of
|
|
20707
|
+
for (const stamp of readdirSync16(root)) {
|
|
20301
20708
|
const dir = join17(root, stamp);
|
|
20302
20709
|
try {
|
|
20303
|
-
const st =
|
|
20710
|
+
const st = statSync14(dir);
|
|
20304
20711
|
if (!st.isDirectory()) continue;
|
|
20305
20712
|
out.push({ stamp, dir, createdMs: st.mtimeMs });
|
|
20306
20713
|
} catch {
|
|
@@ -20316,17 +20723,17 @@ function hasFreshBackup(memoryDir, home = homedir9()) {
|
|
|
20316
20723
|
return backups[0].createdMs >= newestChange;
|
|
20317
20724
|
}
|
|
20318
20725
|
function takeBackup(memoryDir, nowMs, home = homedir9(), retention = DEFAULT_RETENTION2) {
|
|
20319
|
-
if (!
|
|
20726
|
+
if (!existsSync33(memoryDir)) {
|
|
20320
20727
|
throw new BackupError(`memory directory does not exist: ${memoryDir}`);
|
|
20321
20728
|
}
|
|
20322
20729
|
const stamp = backupStamp2(nowMs);
|
|
20323
20730
|
const dir = join17(backupsRoot(home), stamp);
|
|
20324
20731
|
try {
|
|
20325
20732
|
mkdirSync13(dir, { recursive: true, mode: 448 });
|
|
20326
|
-
for (const entry of
|
|
20733
|
+
for (const entry of readdirSync16(memoryDir)) {
|
|
20327
20734
|
const srcPath = join17(memoryDir, entry);
|
|
20328
20735
|
try {
|
|
20329
|
-
if (!
|
|
20736
|
+
if (!statSync14(srcPath).isFile()) continue;
|
|
20330
20737
|
} catch {
|
|
20331
20738
|
continue;
|
|
20332
20739
|
}
|
|
@@ -20354,16 +20761,16 @@ function restoreBackup(memoryDir, opts = {}) {
|
|
|
20354
20761
|
const chosen = opts.from ? backups.find((b2) => b2.stamp === opts.from) : backups[0];
|
|
20355
20762
|
if (!chosen) throw new BackupError(`no backup with stamp: ${opts.from}`);
|
|
20356
20763
|
const wouldRestore = [];
|
|
20357
|
-
for (const entry of
|
|
20764
|
+
for (const entry of readdirSync16(chosen.dir)) {
|
|
20358
20765
|
try {
|
|
20359
|
-
if (
|
|
20766
|
+
if (statSync14(join17(chosen.dir, entry)).isFile()) wouldRestore.push(entry);
|
|
20360
20767
|
} catch {
|
|
20361
20768
|
continue;
|
|
20362
20769
|
}
|
|
20363
20770
|
}
|
|
20364
|
-
const currentFiles =
|
|
20771
|
+
const currentFiles = existsSync33(memoryDir) ? readdirSync16(memoryDir).filter((e2) => {
|
|
20365
20772
|
try {
|
|
20366
|
-
return
|
|
20773
|
+
return statSync14(join17(memoryDir, e2)).isFile();
|
|
20367
20774
|
} catch {
|
|
20368
20775
|
return false;
|
|
20369
20776
|
}
|
|
@@ -20407,7 +20814,7 @@ var init_memory_backup = __esm({
|
|
|
20407
20814
|
});
|
|
20408
20815
|
|
|
20409
20816
|
// src/memory-index-region.ts
|
|
20410
|
-
import { readFileSync as
|
|
20817
|
+
import { readFileSync as readFileSync25, existsSync as existsSync34 } from "fs";
|
|
20411
20818
|
import { dirname as dirname16, join as join18 } from "path";
|
|
20412
20819
|
function parseRegion(content) {
|
|
20413
20820
|
const begins = countOccurrences(content, BEGIN_SENTINEL);
|
|
@@ -20512,9 +20919,9 @@ function withMemoryIndexLock(memoryDir, fn) {
|
|
|
20512
20919
|
});
|
|
20513
20920
|
}
|
|
20514
20921
|
function readMemoryIndex(indexPath) {
|
|
20515
|
-
if (!
|
|
20922
|
+
if (!existsSync34(indexPath)) return void 0;
|
|
20516
20923
|
try {
|
|
20517
|
-
return
|
|
20924
|
+
return readFileSync25(indexPath, "utf8");
|
|
20518
20925
|
} catch {
|
|
20519
20926
|
return void 0;
|
|
20520
20927
|
}
|
|
@@ -20585,7 +20992,7 @@ var init_memory_files_config = __esm({
|
|
|
20585
20992
|
});
|
|
20586
20993
|
|
|
20587
20994
|
// src/memory-renderer.ts
|
|
20588
|
-
import { existsSync as
|
|
20995
|
+
import { existsSync as existsSync35, readFileSync as readFileSync26 } from "fs";
|
|
20589
20996
|
import { join as join19 } from "path";
|
|
20590
20997
|
import { homedir as homedir10 } from "os";
|
|
20591
20998
|
import { createHash as createHash8 } from "crypto";
|
|
@@ -20600,7 +21007,7 @@ function renderMemoryFiles(db, candidates, opts) {
|
|
|
20600
21007
|
const audit = opts.audit ?? (() => {
|
|
20601
21008
|
});
|
|
20602
21009
|
const { memoryDir } = opts;
|
|
20603
|
-
if (!
|
|
21010
|
+
if (!existsSync35(memoryDir)) {
|
|
20604
21011
|
return EMPTY("no_memory_dir", candidates.length);
|
|
20605
21012
|
}
|
|
20606
21013
|
try {
|
|
@@ -20671,8 +21078,8 @@ function renderLocked(db, candidates, o) {
|
|
|
20671
21078
|
refusals.push({ name: c2.name, reason: "tombstoned" });
|
|
20672
21079
|
continue;
|
|
20673
21080
|
}
|
|
20674
|
-
if (
|
|
20675
|
-
const existing =
|
|
21081
|
+
if (existsSync35(absPath)) {
|
|
21082
|
+
const existing = readFileSync26(absPath, "utf8");
|
|
20676
21083
|
const fm = readMemoryFileFrontmatter(existing);
|
|
20677
21084
|
const storeRow = db.prepare(
|
|
20678
21085
|
`SELECT massu_authored, massu_render_mac, adopted_human_at_epoch
|
|
@@ -20932,16 +21339,16 @@ var init_memory_renderer = __esm({
|
|
|
20932
21339
|
});
|
|
20933
21340
|
|
|
20934
21341
|
// src/commands/memory-render-cli.ts
|
|
20935
|
-
import { readdirSync as
|
|
21342
|
+
import { readdirSync as readdirSync17, readFileSync as readFileSync27, existsSync as existsSync36, rmSync as rmSync8, statSync as statSync15 } from "fs";
|
|
20936
21343
|
import { join as join20 } from "path";
|
|
20937
21344
|
import { homedir as homedir11 } from "os";
|
|
20938
21345
|
function memoryFiles(memoryDir) {
|
|
20939
|
-
if (!
|
|
20940
|
-
return
|
|
21346
|
+
if (!existsSync36(memoryDir)) return [];
|
|
21347
|
+
return readdirSync17(memoryDir).filter((f2) => {
|
|
20941
21348
|
if (!f2.endsWith(".md")) return false;
|
|
20942
21349
|
if (f2 === "MEMORY.md") return false;
|
|
20943
21350
|
try {
|
|
20944
|
-
return
|
|
21351
|
+
return statSync15(join20(memoryDir, f2)).isFile();
|
|
20945
21352
|
} catch {
|
|
20946
21353
|
return false;
|
|
20947
21354
|
}
|
|
@@ -20952,7 +21359,7 @@ function massuAuthoredFiles(db, memoryDir, home = homedir11()) {
|
|
|
20952
21359
|
for (const f2 of memoryFiles(memoryDir)) {
|
|
20953
21360
|
let content;
|
|
20954
21361
|
try {
|
|
20955
|
-
content =
|
|
21362
|
+
content = readFileSync27(join20(memoryDir, f2), "utf8");
|
|
20956
21363
|
} catch {
|
|
20957
21364
|
continue;
|
|
20958
21365
|
}
|
|
@@ -20980,7 +21387,7 @@ function memoryAdopt(db, memoryDir, opts = {}) {
|
|
|
20980
21387
|
for (const f2 of memoryFiles(memoryDir)) {
|
|
20981
21388
|
const row = db.prepare(`SELECT id FROM memory_files WHERE rel_path = ? COLLATE NOCASE`).get(f2);
|
|
20982
21389
|
if (row) continue;
|
|
20983
|
-
const content =
|
|
21390
|
+
const content = readFileSync27(join20(memoryDir, f2), "utf8");
|
|
20984
21391
|
if (verifyAuthorship(stripFrontmatter(content), readMemoryFileFrontmatter(content), null, home)) {
|
|
20985
21392
|
candidates.push(f2);
|
|
20986
21393
|
}
|
|
@@ -21010,7 +21417,7 @@ function memoryAdopt(db, memoryDir, opts = {}) {
|
|
|
21010
21417
|
db.exec("BEGIN");
|
|
21011
21418
|
try {
|
|
21012
21419
|
for (const f2 of candidates) {
|
|
21013
|
-
const content =
|
|
21420
|
+
const content = readFileSync27(join20(memoryDir, f2), "utf8");
|
|
21014
21421
|
const fm = readMemoryFileFrontmatter(content);
|
|
21015
21422
|
insert.run(
|
|
21016
21423
|
f2,
|
|
@@ -21175,7 +21582,7 @@ var init_memory_render_candidates = __esm({
|
|
|
21175
21582
|
});
|
|
21176
21583
|
|
|
21177
21584
|
// src/capability-advisor.ts
|
|
21178
|
-
import { existsSync as
|
|
21585
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync14, readFileSync as readFileSync28, writeFileSync as writeFileSync8 } from "fs";
|
|
21179
21586
|
import { homedir as homedir12 } from "os";
|
|
21180
21587
|
import { dirname as dirname17, join as join21 } from "path";
|
|
21181
21588
|
function advisorStatePath(home = homedir12()) {
|
|
@@ -21184,8 +21591,8 @@ function advisorStatePath(home = homedir12()) {
|
|
|
21184
21591
|
function readAdvisorState(home) {
|
|
21185
21592
|
try {
|
|
21186
21593
|
const p19 = advisorStatePath(home);
|
|
21187
|
-
if (!
|
|
21188
|
-
return JSON.parse(
|
|
21594
|
+
if (!existsSync37(p19)) return {};
|
|
21595
|
+
return JSON.parse(readFileSync28(p19, "utf-8"));
|
|
21189
21596
|
} catch {
|
|
21190
21597
|
return {};
|
|
21191
21598
|
}
|
|
@@ -21433,9 +21840,9 @@ var init_memory_repo_identity = __esm({
|
|
|
21433
21840
|
// src/memory-repos-registry.ts
|
|
21434
21841
|
import {
|
|
21435
21842
|
chmodSync as chmodSync7,
|
|
21436
|
-
existsSync as
|
|
21843
|
+
existsSync as existsSync38,
|
|
21437
21844
|
mkdirSync as mkdirSync15,
|
|
21438
|
-
readFileSync as
|
|
21845
|
+
readFileSync as readFileSync29,
|
|
21439
21846
|
writeFileSync as writeFileSync9
|
|
21440
21847
|
} from "fs";
|
|
21441
21848
|
import { homedir as homedir13 } from "os";
|
|
@@ -21448,9 +21855,9 @@ function emptyRegistry() {
|
|
|
21448
21855
|
}
|
|
21449
21856
|
function readReposRegistry(home = homedir13()) {
|
|
21450
21857
|
const p19 = reposRegistryPath(home);
|
|
21451
|
-
if (!
|
|
21858
|
+
if (!existsSync38(p19)) return emptyRegistry();
|
|
21452
21859
|
try {
|
|
21453
|
-
const parsed = JSON.parse(
|
|
21860
|
+
const parsed = JSON.parse(readFileSync29(p19, "utf-8"));
|
|
21454
21861
|
if (!parsed || typeof parsed !== "object") return emptyRegistry();
|
|
21455
21862
|
const r2 = parsed;
|
|
21456
21863
|
if (!Array.isArray(r2.repos)) return emptyRegistry();
|
|
@@ -21498,9 +21905,9 @@ var init_shared_memory_envelope = __esm({
|
|
|
21498
21905
|
// src/security/local-share-signer.ts
|
|
21499
21906
|
import {
|
|
21500
21907
|
chmodSync as chmodSync8,
|
|
21501
|
-
existsSync as
|
|
21908
|
+
existsSync as existsSync39,
|
|
21502
21909
|
mkdirSync as mkdirSync16,
|
|
21503
|
-
readFileSync as
|
|
21910
|
+
readFileSync as readFileSync30,
|
|
21504
21911
|
writeFileSync as writeFileSync10
|
|
21505
21912
|
} from "fs";
|
|
21506
21913
|
import { homedir as homedir14 } from "os";
|
|
@@ -21519,7 +21926,7 @@ function localSharePubKeyPath(home = homedir14()) {
|
|
|
21519
21926
|
return join23(localShareKeyDir(home), "local-share.pub");
|
|
21520
21927
|
}
|
|
21521
21928
|
function readLocalSharePublicKeyRaw(home = homedir14()) {
|
|
21522
|
-
const pubPem =
|
|
21929
|
+
const pubPem = readFileSync30(localSharePubKeyPath(home), "utf-8");
|
|
21523
21930
|
const der = createPublicKey2(pubPem).export({ format: "der", type: "spki" });
|
|
21524
21931
|
return Uint8Array.from(der.subarray(SPKI_ED25519_PREFIX_LEN));
|
|
21525
21932
|
}
|
|
@@ -21938,26 +22345,6 @@ var init_memory_share_cli = __esm({
|
|
|
21938
22345
|
}
|
|
21939
22346
|
});
|
|
21940
22347
|
|
|
21941
|
-
// src/lib/corrections-md.ts
|
|
21942
|
-
function formatCorrectionEntry(e2) {
|
|
21943
|
-
const one = (s) => s.replace(/\s*\n+\s*/g, " ").trim();
|
|
21944
|
-
const lines = [`### ${e2.date} - ${one(e2.title)}`];
|
|
21945
|
-
if (e2.wrong) lines.push(`- **Wrong**: ${one(e2.wrong)}`);
|
|
21946
|
-
if (e2.correction) lines.push(`- **Correction**: ${one(e2.correction)}`);
|
|
21947
|
-
lines.push(`- **Rule**: ${one(e2.rule)}`);
|
|
21948
|
-
for (const [k3, v3] of Object.entries(e2.extra ?? {})) {
|
|
21949
|
-
lines.push(`- ${k3}: ${one(String(v3))}`);
|
|
21950
|
-
}
|
|
21951
|
-
return `
|
|
21952
|
-
${lines.join("\n")}
|
|
21953
|
-
`;
|
|
21954
|
-
}
|
|
21955
|
-
var init_corrections_md = __esm({
|
|
21956
|
-
"src/lib/corrections-md.ts"() {
|
|
21957
|
-
"use strict";
|
|
21958
|
-
}
|
|
21959
|
-
});
|
|
21960
|
-
|
|
21961
22348
|
// src/audit-trail.ts
|
|
21962
22349
|
function p2(baseName) {
|
|
21963
22350
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
@@ -22458,7 +22845,7 @@ var init_promotion_apply_mac = __esm({
|
|
|
22458
22845
|
});
|
|
22459
22846
|
|
|
22460
22847
|
// src/rule-candidate-applier.ts
|
|
22461
|
-
import { readFileSync as
|
|
22848
|
+
import { readFileSync as readFileSync31, writeFileSync as writeFileSync11, existsSync as existsSync40, unlinkSync as unlinkSync5, mkdirSync as mkdirSync17 } from "fs";
|
|
22462
22849
|
import { join as join24, dirname as dirname19, isAbsolute as isAbsolute2 } from "path";
|
|
22463
22850
|
import { homedir as homedir19 } from "os";
|
|
22464
22851
|
import { createHash as createHash11 } from "crypto";
|
|
@@ -22491,7 +22878,7 @@ function writeIndexLineInRegion(indexPath, line) {
|
|
|
22491
22878
|
function correctionsMdTargets(slug, projectRoot, home) {
|
|
22492
22879
|
const dir = resolveMemoryDir(projectRoot, home);
|
|
22493
22880
|
return {
|
|
22494
|
-
correctionsMd:
|
|
22881
|
+
correctionsMd: correctionsPath(dir),
|
|
22495
22882
|
feedbackMd: join24(dir, `feedback_${slug}.md`),
|
|
22496
22883
|
memoryIndex: join24(dir, "MEMORY.md")
|
|
22497
22884
|
};
|
|
@@ -22501,8 +22888,8 @@ function readCandidate(candidateId, projectRoot = getProjectRoot()) {
|
|
|
22501
22888
|
throw new InvalidCandidateIdError(candidateId);
|
|
22502
22889
|
}
|
|
22503
22890
|
const path = join24(projectRoot, ".massu", "rule-candidates", `${candidateId}.json`);
|
|
22504
|
-
if (!
|
|
22505
|
-
const raw =
|
|
22891
|
+
if (!existsSync40(path)) throw new CandidateNotFoundError(path);
|
|
22892
|
+
const raw = readFileSync31(path, "utf-8");
|
|
22506
22893
|
let parsed;
|
|
22507
22894
|
try {
|
|
22508
22895
|
parsed = JSON.parse(raw);
|
|
@@ -22645,7 +23032,7 @@ async function applyRuleCandidate(db, opts) {
|
|
|
22645
23032
|
throw new InvalidSlugError(slug);
|
|
22646
23033
|
}
|
|
22647
23034
|
const memoryIndex = resolveMemoryIndexPath(projectRoot, home);
|
|
22648
|
-
if (!
|
|
23035
|
+
if (!existsSync40(memoryIndex)) {
|
|
22649
23036
|
throw new MemoryIndexMissingError(memoryIndex);
|
|
22650
23037
|
}
|
|
22651
23038
|
let destTargets;
|
|
@@ -22701,7 +23088,7 @@ async function applyRuleCandidate(db, opts) {
|
|
|
22701
23088
|
throw err;
|
|
22702
23089
|
}
|
|
22703
23090
|
}
|
|
22704
|
-
if (
|
|
23091
|
+
if (existsSync40(candidatePath)) unlinkSync5(candidatePath);
|
|
22705
23092
|
let teamShared = false;
|
|
22706
23093
|
const localOrigin = candidate.provenance === void 0;
|
|
22707
23094
|
const entitledTeam = entitledForTeamSharedPromotion(ent.currentTier);
|
|
@@ -22792,7 +23179,7 @@ function writeDestination(opts, candidate, slug, targets, auditRowId) {
|
|
|
22792
23179
|
function writeCorrectionsMd(opts, candidate, slug, targets, auditRowId) {
|
|
22793
23180
|
const [correctionsMd, feedbackMd] = targets.paths;
|
|
22794
23181
|
const dateIso = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
22795
|
-
const
|
|
23182
|
+
const correctionsEntryData = {
|
|
22796
23183
|
date: dateIso,
|
|
22797
23184
|
title: slug,
|
|
22798
23185
|
rule: opts.draftText,
|
|
@@ -22801,10 +23188,10 @@ function writeCorrectionsMd(opts, candidate, slug, targets, auditRowId) {
|
|
|
22801
23188
|
score: candidate.score,
|
|
22802
23189
|
audit_log_id: auditRowId
|
|
22803
23190
|
}
|
|
22804
|
-
}
|
|
22805
|
-
if (!
|
|
22806
|
-
const correctionsPre =
|
|
22807
|
-
writeFileSync11(correctionsMd, correctionsPre
|
|
23191
|
+
};
|
|
23192
|
+
if (!existsSync40(dirname19(correctionsMd))) mkdirSync17(dirname19(correctionsMd), { recursive: true });
|
|
23193
|
+
const correctionsPre = existsSync40(correctionsMd) ? readFileSync31(correctionsMd, "utf-8") : "";
|
|
23194
|
+
writeFileSync11(correctionsMd, insertCorrectionEntry(correctionsPre, correctionsEntryData), "utf-8");
|
|
22808
23195
|
const body = `${opts.draftText}
|
|
22809
23196
|
`;
|
|
22810
23197
|
const mac = mintAuthorship(body);
|
|
@@ -22829,7 +23216,7 @@ function writeCorrectionsMd(opts, candidate, slug, targets, auditRowId) {
|
|
|
22829
23216
|
function writePatternScanner(opts, candidate, slug, targets) {
|
|
22830
23217
|
const [scannerPath, driftGuardPath] = targets.paths;
|
|
22831
23218
|
const checkNumber = opts.patternScannerCheckNumber;
|
|
22832
|
-
const scannerPre =
|
|
23219
|
+
const scannerPre = existsSync40(scannerPath) ? readFileSync31(scannerPath, "utf-8") : "";
|
|
22833
23220
|
if (scannerPre.includes(`${SCANNER_AUTOGEN_MARKER_PREFIX} ${candidate.prompt_hash}`)) {
|
|
22834
23221
|
throw new DestinationConfigError(
|
|
22835
23222
|
`scanner already contains a check auto-generated for prompt_hash ${candidate.prompt_hash}`
|
|
@@ -22865,13 +23252,13 @@ function writePatternScanner(opts, candidate, slug, targets) {
|
|
|
22865
23252
|
`});`,
|
|
22866
23253
|
""
|
|
22867
23254
|
].join("\n");
|
|
22868
|
-
if (!
|
|
23255
|
+
if (!existsSync40(dirname19(driftGuardPath))) mkdirSync17(dirname19(driftGuardPath), { recursive: true });
|
|
22869
23256
|
writeFileSync11(driftGuardPath, driftGuard, "utf-8");
|
|
22870
23257
|
}
|
|
22871
23258
|
function writeClaudeMdCr(opts, candidate, slug, targets) {
|
|
22872
23259
|
const [claudePath] = targets.paths;
|
|
22873
23260
|
const crNumber = opts.claudeMdCrNumber;
|
|
22874
|
-
const pre =
|
|
23261
|
+
const pre = existsSync40(claudePath) ? readFileSync31(claudePath, "utf-8") : "";
|
|
22875
23262
|
if (new RegExp(`\\bCR-${crNumber}\\b`).test(pre)) {
|
|
22876
23263
|
throw new DestinationConfigError(`CLAUDE.md already contains a CR-${crNumber} entry`);
|
|
22877
23264
|
}
|
|
@@ -22913,8 +23300,8 @@ function writeCustomDestination(opts, candidate, slug, targets) {
|
|
|
22913
23300
|
destination_name: entry.name
|
|
22914
23301
|
};
|
|
22915
23302
|
const rendered = renderTemplate2(entry.template, vars);
|
|
22916
|
-
if (!
|
|
22917
|
-
const pre =
|
|
23303
|
+
if (!existsSync40(dirname19(destPath))) mkdirSync17(dirname19(destPath), { recursive: true });
|
|
23304
|
+
const pre = existsSync40(destPath) ? readFileSync31(destPath, "utf-8") : "";
|
|
22918
23305
|
const sep4 = pre && !pre.endsWith("\n") ? "\n" : "";
|
|
22919
23306
|
writeFileSync11(destPath, pre + sep4 + rendered + (rendered.endsWith("\n") ? "" : "\n"), "utf-8");
|
|
22920
23307
|
}
|
|
@@ -22967,8 +23354,8 @@ function dismissRuleCandidate(db, opts) {
|
|
|
22967
23354
|
}
|
|
22968
23355
|
try {
|
|
22969
23356
|
const dismissedLog = join24(projectRoot, ".massu", "rule-candidates", ".dismissed.jsonl");
|
|
22970
|
-
if (!
|
|
22971
|
-
const pre =
|
|
23357
|
+
if (!existsSync40(dirname19(dismissedLog))) mkdirSync17(dirname19(dismissedLog), { recursive: true });
|
|
23358
|
+
const pre = existsSync40(dismissedLog) ? readFileSync31(dismissedLog, "utf-8") : "";
|
|
22972
23359
|
const sep4 = pre && !pre.endsWith("\n") ? "\n" : "";
|
|
22973
23360
|
writeFileSync11(dismissedLog, pre + sep4 + JSON.stringify({
|
|
22974
23361
|
id: opts.candidateId,
|
|
@@ -22979,7 +23366,7 @@ function dismissRuleCandidate(db, opts) {
|
|
|
22979
23366
|
audit_log_id: auditRowId,
|
|
22980
23367
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
22981
23368
|
}) + "\n", "utf-8");
|
|
22982
|
-
if (
|
|
23369
|
+
if (existsSync40(candidatePath)) unlinkSync5(candidatePath);
|
|
22983
23370
|
} catch (err) {
|
|
22984
23371
|
appendFailureLog(projectRoot, {
|
|
22985
23372
|
candidate_id: opts.candidateId,
|
|
@@ -22998,9 +23385,9 @@ function dismissRuleCandidate(db, opts) {
|
|
|
22998
23385
|
function appendFailureLog(projectRoot, entry) {
|
|
22999
23386
|
try {
|
|
23000
23387
|
const dir = join24(projectRoot, ".massu", "rule-candidates");
|
|
23001
|
-
if (!
|
|
23388
|
+
if (!existsSync40(dir)) mkdirSync17(dir, { recursive: true });
|
|
23002
23389
|
const logPath = join24(dir, ".failures.jsonl");
|
|
23003
|
-
const pre =
|
|
23390
|
+
const pre = existsSync40(logPath) ? readFileSync31(logPath, "utf-8") : "";
|
|
23004
23391
|
const sep4 = pre && !pre.endsWith("\n") ? "\n" : "";
|
|
23005
23392
|
writeFileSync11(logPath, pre + sep4 + JSON.stringify(entry) + "\n", "utf-8");
|
|
23006
23393
|
} catch {
|
|
@@ -23085,7 +23472,7 @@ var init_rule_candidate_applier = __esm({
|
|
|
23085
23472
|
});
|
|
23086
23473
|
|
|
23087
23474
|
// src/rule-candidate-preview.ts
|
|
23088
|
-
import { readFileSync as
|
|
23475
|
+
import { readFileSync as readFileSync32, writeFileSync as writeFileSync12, existsSync as existsSync41 } from "node:fs";
|
|
23089
23476
|
function renderHardenedPreview(destination, body) {
|
|
23090
23477
|
const rendered = String(body ?? "");
|
|
23091
23478
|
const riskFindings = RISK_PATTERNS.filter((p19) => p19.re.test(rendered)).map((p19) => p19.label);
|
|
@@ -23113,11 +23500,11 @@ function validateReviewAttestation(att) {
|
|
|
23113
23500
|
};
|
|
23114
23501
|
}
|
|
23115
23502
|
function recordHardenedReviewAttestation(sidecarPath2, attestation) {
|
|
23116
|
-
if (!
|
|
23503
|
+
if (!existsSync41(sidecarPath2)) {
|
|
23117
23504
|
throw new Error(`candidate sidecar not found: ${sidecarPath2}`);
|
|
23118
23505
|
}
|
|
23119
23506
|
const validated = validateReviewAttestation(attestation);
|
|
23120
|
-
const raw =
|
|
23507
|
+
const raw = readFileSync32(sidecarPath2, "utf-8");
|
|
23121
23508
|
const sidecar = JSON.parse(raw);
|
|
23122
23509
|
const prov = sidecar.provenance;
|
|
23123
23510
|
if (!prov || prov.origin !== "team" || prov.hardened !== true) {
|
|
@@ -23225,7 +23612,7 @@ var init_rule_candidate_renderer = __esm({
|
|
|
23225
23612
|
});
|
|
23226
23613
|
|
|
23227
23614
|
// src/rule-promotion-effectiveness.ts
|
|
23228
|
-
import { readFileSync as
|
|
23615
|
+
import { readFileSync as readFileSync33, existsSync as existsSync42 } from "fs";
|
|
23229
23616
|
function evaluateCr53Effectiveness(inputs) {
|
|
23230
23617
|
const { db, failureLogPath, knownLimitations } = inputs;
|
|
23231
23618
|
const allowlist = new Set(knownLimitations.map((k3) => k3.promptHash));
|
|
@@ -23256,9 +23643,9 @@ function evaluateCr53Effectiveness(inputs) {
|
|
|
23256
23643
|
});
|
|
23257
23644
|
}
|
|
23258
23645
|
const failureLogViolations = [];
|
|
23259
|
-
if (
|
|
23646
|
+
if (existsSync42(failureLogPath)) {
|
|
23260
23647
|
try {
|
|
23261
|
-
const raw =
|
|
23648
|
+
const raw = readFileSync33(failureLogPath, "utf-8");
|
|
23262
23649
|
const cutoff = Date.now() - SEVEN_DAYS_MS;
|
|
23263
23650
|
for (const line of raw.split("\n")) {
|
|
23264
23651
|
const trimmed = line.trim();
|
|
@@ -23435,7 +23822,7 @@ var init_rule_pack_schema = __esm({
|
|
|
23435
23822
|
});
|
|
23436
23823
|
|
|
23437
23824
|
// src/rule-pack-sync.ts
|
|
23438
|
-
import { existsSync as
|
|
23825
|
+
import { existsSync as existsSync43, writeFileSync as writeFileSync13, mkdirSync as mkdirSync18 } from "fs";
|
|
23439
23826
|
import { homedir as homedir20 } from "os";
|
|
23440
23827
|
import { join as join25, dirname as dirname20 } from "path";
|
|
23441
23828
|
async function pullInstalledPackRules(db, opts = {}) {
|
|
@@ -23500,7 +23887,7 @@ async function pullInstalledPackRules(db, opts = {}) {
|
|
|
23500
23887
|
result.pulled += 1;
|
|
23501
23888
|
const promptHash = packRulePromptHash(pack.slug, rule);
|
|
23502
23889
|
const candidatePath = sidecarPath(projectRoot, promptHash);
|
|
23503
|
-
if (
|
|
23890
|
+
if (existsSync43(candidatePath) || alreadyApplied(db, promptHash)) {
|
|
23504
23891
|
result.skipped += 1;
|
|
23505
23892
|
continue;
|
|
23506
23893
|
}
|
|
@@ -23593,7 +23980,7 @@ function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack
|
|
|
23593
23980
|
draft_text: draftText
|
|
23594
23981
|
};
|
|
23595
23982
|
const dir = dirname20(candidatePath);
|
|
23596
|
-
if (!
|
|
23983
|
+
if (!existsSync43(dir)) mkdirSync18(dir, { recursive: true });
|
|
23597
23984
|
writeFileSync13(candidatePath, JSON.stringify(sidecar, null, 2), "utf-8");
|
|
23598
23985
|
}
|
|
23599
23986
|
function emitDropTelemetry(db, eventType, data) {
|
|
@@ -24465,7 +24852,7 @@ var init_memory_supersede = __esm({
|
|
|
24465
24852
|
|
|
24466
24853
|
// src/memory-consolidate.ts
|
|
24467
24854
|
import { createHash as createHash12 } from "crypto";
|
|
24468
|
-
import { existsSync as
|
|
24855
|
+
import { existsSync as existsSync44, mkdirSync as mkdirSync19, writeFileSync as writeFileSync14 } from "fs";
|
|
24469
24856
|
import { join as join27 } from "path";
|
|
24470
24857
|
function emptyResult() {
|
|
24471
24858
|
return {
|
|
@@ -24642,7 +25029,7 @@ ${representative.detail}` : ""}`;
|
|
|
24642
25029
|
const clusterKey = cluster.map((c2) => c2.title.toLowerCase().replace(/\s+/g, " ").trim()).sort().join("|");
|
|
24643
25030
|
const promptHash = createHash12("sha256").update(clusterKey).digest("hex").slice(0, 16);
|
|
24644
25031
|
const candidatePath = join27(candidateDir, `${promptHash}.json`);
|
|
24645
|
-
if (
|
|
25032
|
+
if (existsSync44(candidatePath)) continue;
|
|
24646
25033
|
if (dryRun) {
|
|
24647
25034
|
promoted++;
|
|
24648
25035
|
continue;
|
|
@@ -24935,8 +25322,6 @@ var show_template_exports = {};
|
|
|
24935
25322
|
__export(show_template_exports, {
|
|
24936
25323
|
runShowTemplate: () => runShowTemplate
|
|
24937
25324
|
});
|
|
24938
|
-
import { existsSync as existsSync43, readFileSync as readFileSync32 } from "fs";
|
|
24939
|
-
import { resolve as resolve17 } from "path";
|
|
24940
25325
|
function normalizeBaseName(input) {
|
|
24941
25326
|
return input.endsWith(".md") ? input.slice(0, -".md".length) : input;
|
|
24942
25327
|
}
|
|
@@ -24956,28 +25341,34 @@ async function runShowTemplate(args2) {
|
|
|
24956
25341
|
return;
|
|
24957
25342
|
}
|
|
24958
25343
|
const framework = getConfig().framework;
|
|
24959
|
-
const
|
|
24960
|
-
|
|
25344
|
+
const rendered = renderResolvedAsset(
|
|
25345
|
+
baseName,
|
|
25346
|
+
sourceDir,
|
|
25347
|
+
framework,
|
|
25348
|
+
makeRenderContext(framework)
|
|
25349
|
+
);
|
|
25350
|
+
if (rendered.kind === "miss") {
|
|
24961
25351
|
process.stderr.write(`massu: no template named "${baseName}" found
|
|
24962
25352
|
`);
|
|
24963
25353
|
process.exit(1);
|
|
24964
25354
|
return;
|
|
24965
25355
|
}
|
|
24966
|
-
|
|
24967
|
-
|
|
24968
|
-
|
|
24969
|
-
|
|
24970
|
-
|
|
25356
|
+
if (rendered.kind === "error") {
|
|
25357
|
+
process.stderr.write(
|
|
25358
|
+
`massu: template "${baseName}" could not be rendered: ${rendered.message}
|
|
25359
|
+
`
|
|
25360
|
+
);
|
|
24971
25361
|
process.exit(1);
|
|
24972
25362
|
return;
|
|
24973
25363
|
}
|
|
24974
|
-
process.stdout.write(
|
|
25364
|
+
process.stdout.write(rendered.content);
|
|
24975
25365
|
}
|
|
24976
25366
|
var init_show_template = __esm({
|
|
24977
25367
|
"src/commands/show-template.ts"() {
|
|
24978
25368
|
"use strict";
|
|
24979
25369
|
init_config();
|
|
24980
|
-
|
|
25370
|
+
init_asset_resolution();
|
|
25371
|
+
init_asset_render();
|
|
24981
25372
|
}
|
|
24982
25373
|
});
|
|
24983
25374
|
|
|
@@ -25024,9 +25415,9 @@ var init_passthrough = __esm({
|
|
|
25024
25415
|
});
|
|
25025
25416
|
|
|
25026
25417
|
// src/lib/installLock.ts
|
|
25027
|
-
import { resolve as
|
|
25418
|
+
import { resolve as resolve20 } from "path";
|
|
25028
25419
|
function withInstallLock(projectRoot, fn, opts = {}) {
|
|
25029
|
-
const lockPath =
|
|
25420
|
+
const lockPath = resolve20(projectRoot, ".massu", "installAll.lock");
|
|
25030
25421
|
return withFileLockSync(
|
|
25031
25422
|
lockPath,
|
|
25032
25423
|
fn,
|
|
@@ -25066,8 +25457,8 @@ __export(config_refresh_exports, {
|
|
|
25066
25457
|
mergeRefresh: () => mergeRefresh,
|
|
25067
25458
|
runConfigRefresh: () => runConfigRefresh
|
|
25068
25459
|
});
|
|
25069
|
-
import { existsSync as
|
|
25070
|
-
import { resolve as
|
|
25460
|
+
import { existsSync as existsSync45, readFileSync as readFileSync34, rmSync as rmSync9 } from "fs";
|
|
25461
|
+
import { resolve as resolve21 } from "path";
|
|
25071
25462
|
import { parse as parseYaml5 } from "yaml";
|
|
25072
25463
|
function flatten(obj, prefix3 = "") {
|
|
25073
25464
|
const out = {};
|
|
@@ -25195,17 +25586,17 @@ function renderDiff(diff) {
|
|
|
25195
25586
|
}
|
|
25196
25587
|
async function runConfigRefresh(opts = {}) {
|
|
25197
25588
|
const cwd = opts.cwd ?? process.cwd();
|
|
25198
|
-
const configPath =
|
|
25589
|
+
const configPath = resolve21(cwd, "massu.config.yaml");
|
|
25199
25590
|
const log = opts.silent ? () => {
|
|
25200
25591
|
} : (s) => process.stdout.write(s);
|
|
25201
|
-
if (!
|
|
25592
|
+
if (!existsSync45(configPath)) {
|
|
25202
25593
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
25203
25594
|
if (!opts.silent) process.stderr.write(message + "\n");
|
|
25204
25595
|
return { exitCode: 1, applied: false, dryRun: !!opts.dryRun, diff: [], message };
|
|
25205
25596
|
}
|
|
25206
25597
|
let existing;
|
|
25207
25598
|
try {
|
|
25208
|
-
const content =
|
|
25599
|
+
const content = readFileSync34(configPath, "utf-8");
|
|
25209
25600
|
const parsed = parseYaml5(content);
|
|
25210
25601
|
if (!parsed || typeof parsed !== "object") {
|
|
25211
25602
|
throw new Error("config is not a YAML object");
|
|
@@ -25278,8 +25669,8 @@ async function runConfigRefresh(opts = {}) {
|
|
|
25278
25669
|
`);
|
|
25279
25670
|
const stackResolved = installResult.totalInstalled > 0 || installResult.totalUpdated > 0;
|
|
25280
25671
|
if (stackResolved) {
|
|
25281
|
-
const placeholderPath =
|
|
25282
|
-
if (
|
|
25672
|
+
const placeholderPath = resolve21(installResult.claudeDir, "commands", "_massu-needs-stack.md");
|
|
25673
|
+
if (existsSync45(placeholderPath)) {
|
|
25283
25674
|
try {
|
|
25284
25675
|
rmSync9(placeholderPath, { force: true });
|
|
25285
25676
|
log("Removed _massu-needs-stack.md (stack now declared).\n");
|
|
@@ -25389,7 +25780,7 @@ var init_gitToplevel = __esm({
|
|
|
25389
25780
|
|
|
25390
25781
|
// src/watch/canary.ts
|
|
25391
25782
|
import { mkdirSync as mkdirSync20, rmSync as rmSync10, writeFileSync as writeFileSync15 } from "fs";
|
|
25392
|
-
import { isAbsolute as isAbsolute3, relative as relative7, resolve as
|
|
25783
|
+
import { isAbsolute as isAbsolute3, relative as relative7, resolve as resolve22, sep as sep2 } from "path";
|
|
25393
25784
|
function toRelative(projectRoot, p19) {
|
|
25394
25785
|
const raw = String(p19);
|
|
25395
25786
|
const rel = isAbsolute3(raw) ? relative7(projectRoot, raw) : raw;
|
|
@@ -25409,9 +25800,9 @@ function canaryAwareIgnore(ignore, projectRoot) {
|
|
|
25409
25800
|
return [...withoutMassu, massuButNotCanary];
|
|
25410
25801
|
}
|
|
25411
25802
|
function createWatcherCanary(opts) {
|
|
25412
|
-
const projectRoot =
|
|
25803
|
+
const projectRoot = resolve22(opts.projectRoot);
|
|
25413
25804
|
const now = opts.now ?? Date.now;
|
|
25414
|
-
const filePath =
|
|
25805
|
+
const filePath = resolve22(projectRoot, CANARY_FILE_REL);
|
|
25415
25806
|
let outstanding = false;
|
|
25416
25807
|
let echoed = false;
|
|
25417
25808
|
let consecutiveDead = 0;
|
|
@@ -25425,7 +25816,7 @@ function createWatcherCanary(opts) {
|
|
|
25425
25816
|
arm() {
|
|
25426
25817
|
if (outstanding) return "armed";
|
|
25427
25818
|
try {
|
|
25428
|
-
mkdirSync20(
|
|
25819
|
+
mkdirSync20(resolve22(projectRoot, CANARY_DIR_REL), { recursive: true });
|
|
25429
25820
|
writeFileSync15(filePath, `${now()}
|
|
25430
25821
|
`, "utf-8");
|
|
25431
25822
|
} catch {
|
|
@@ -25478,14 +25869,14 @@ var init_canary = __esm({
|
|
|
25478
25869
|
});
|
|
25479
25870
|
|
|
25480
25871
|
// src/watch/lockfile-detector.ts
|
|
25481
|
-
import { existsSync as
|
|
25482
|
-
import { resolve as
|
|
25872
|
+
import { existsSync as existsSync46, statSync as statSync16 } from "fs";
|
|
25873
|
+
import { resolve as resolve23 } from "path";
|
|
25483
25874
|
function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WINDOW_MS) {
|
|
25484
25875
|
for (const lf of KNOWN_LOCKFILES) {
|
|
25485
|
-
const p19 =
|
|
25486
|
-
if (!
|
|
25876
|
+
const p19 = resolve23(projectRoot, lf);
|
|
25877
|
+
if (!existsSync46(p19)) continue;
|
|
25487
25878
|
try {
|
|
25488
|
-
const stat =
|
|
25879
|
+
const stat = statSync16(p19);
|
|
25489
25880
|
const delta = now - stat.mtimeMs;
|
|
25490
25881
|
if (delta >= 0 && delta < windowMs) return true;
|
|
25491
25882
|
} catch {
|
|
@@ -25496,7 +25887,7 @@ function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WIN
|
|
|
25496
25887
|
function gitMidOperation(projectRoot) {
|
|
25497
25888
|
const sentinels = ["MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD", "rebase-apply", "rebase-merge"];
|
|
25498
25889
|
for (const s of sentinels) {
|
|
25499
|
-
if (
|
|
25890
|
+
if (existsSync46(resolve23(projectRoot, ".git", s))) return true;
|
|
25500
25891
|
}
|
|
25501
25892
|
return false;
|
|
25502
25893
|
}
|
|
@@ -25695,18 +26086,18 @@ var init_paths = __esm({
|
|
|
25695
26086
|
});
|
|
25696
26087
|
|
|
25697
26088
|
// src/watch/state.ts
|
|
25698
|
-
import { closeSync as closeSync5, existsSync as
|
|
25699
|
-
import { dirname as dirname21, resolve as
|
|
26089
|
+
import { closeSync as closeSync5, existsSync as existsSync47, fsyncSync as fsyncSync5, mkdirSync as mkdirSync21, openSync as openSync5, readFileSync as readFileSync35, renameSync as renameSync5, rmSync as rmSync11, writeFileSync as writeFileSync16, writeSync as writeSync3 } from "fs";
|
|
26090
|
+
import { dirname as dirname21, resolve as resolve24 } from "path";
|
|
25700
26091
|
function watchStatePath(projectRoot) {
|
|
25701
|
-
return
|
|
26092
|
+
return resolve24(projectRoot, ".massu", "watch-state.json");
|
|
25702
26093
|
}
|
|
25703
26094
|
function backupStatePath(projectRoot) {
|
|
25704
|
-
return
|
|
26095
|
+
return resolve24(projectRoot, ".massu", "watch-state.v0.bak.json");
|
|
25705
26096
|
}
|
|
25706
26097
|
function readState(projectRoot) {
|
|
25707
26098
|
const path = watchStatePath(projectRoot);
|
|
25708
|
-
if (!
|
|
25709
|
-
const content =
|
|
26099
|
+
if (!existsSync47(path)) return { ...DEFAULT_STATE };
|
|
26100
|
+
const content = readFileSync35(path, "utf-8");
|
|
25710
26101
|
let raw;
|
|
25711
26102
|
try {
|
|
25712
26103
|
raw = JSON.parse(content);
|
|
@@ -25769,7 +26160,7 @@ function writeStateAtomic(projectRoot, state) {
|
|
|
25769
26160
|
renameSync5(tmp, path);
|
|
25770
26161
|
renamed = true;
|
|
25771
26162
|
} finally {
|
|
25772
|
-
if (!renamed &&
|
|
26163
|
+
if (!renamed && existsSync47(tmp)) {
|
|
25773
26164
|
try {
|
|
25774
26165
|
rmSync11(tmp, { force: true });
|
|
25775
26166
|
} catch {
|
|
@@ -25815,7 +26206,7 @@ var init_state = __esm({
|
|
|
25815
26206
|
// src/watch/daemon.ts
|
|
25816
26207
|
import * as chokidar from "chokidar";
|
|
25817
26208
|
import { mkdirSync as mkdirSync22 } from "fs";
|
|
25818
|
-
import { resolve as
|
|
26209
|
+
import { resolve as resolve25 } from "path";
|
|
25819
26210
|
function readDaemonConfig(projectRoot) {
|
|
25820
26211
|
const cfg = getConfig();
|
|
25821
26212
|
const w2 = cfg.watch ?? {};
|
|
@@ -26106,7 +26497,7 @@ async function startDaemon(projectRoot, hooks) {
|
|
|
26106
26497
|
);
|
|
26107
26498
|
canary = createWatcherCanary({ projectRoot: cfg.projectRoot, now });
|
|
26108
26499
|
try {
|
|
26109
|
-
mkdirSync22(
|
|
26500
|
+
mkdirSync22(resolve25(cfg.projectRoot, canary.dirRel), { recursive: true });
|
|
26110
26501
|
} catch {
|
|
26111
26502
|
}
|
|
26112
26503
|
watchSpec = {
|
|
@@ -26157,8 +26548,8 @@ __export(watch_exports, {
|
|
|
26157
26548
|
runWatch: () => runWatch
|
|
26158
26549
|
});
|
|
26159
26550
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
26160
|
-
import { basename as basename8, dirname as dirname22, resolve as
|
|
26161
|
-
import { appendFileSync as appendFileSync4, existsSync as
|
|
26551
|
+
import { basename as basename8, dirname as dirname22, resolve as resolve26 } from "path";
|
|
26552
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync48, mkdirSync as mkdirSync23, readFileSync as readFileSync36 } from "fs";
|
|
26162
26553
|
function parseFlags(args2) {
|
|
26163
26554
|
const out = {
|
|
26164
26555
|
foreground: false,
|
|
@@ -26183,8 +26574,8 @@ function parseFlags(args2) {
|
|
|
26183
26574
|
}
|
|
26184
26575
|
function findClaudeBg() {
|
|
26185
26576
|
const home = process.env.HOME ?? "";
|
|
26186
|
-
const fixed = home ?
|
|
26187
|
-
if (fixed &&
|
|
26577
|
+
const fixed = home ? resolve26(home, ".claude", "bin", "claude-bg") : null;
|
|
26578
|
+
if (fixed && existsSync48(fixed)) return fixed;
|
|
26188
26579
|
const which = spawnSync5("which", ["claude-bg"], { encoding: "utf-8" });
|
|
26189
26580
|
if (which.status === 0 && which.stdout) {
|
|
26190
26581
|
const p19 = which.stdout.trim();
|
|
@@ -26245,7 +26636,7 @@ async function runWatch(args2) {
|
|
|
26245
26636
|
}
|
|
26246
26637
|
function runStatus(root) {
|
|
26247
26638
|
const path = watchStatePath(root);
|
|
26248
|
-
if (!
|
|
26639
|
+
if (!existsSync48(path)) {
|
|
26249
26640
|
process.stdout.write("massu watch: not running (no state file)\n");
|
|
26250
26641
|
return { exitCode: 0 };
|
|
26251
26642
|
}
|
|
@@ -26327,7 +26718,7 @@ async function runForeground(root) {
|
|
|
26327
26718
|
}
|
|
26328
26719
|
throw err;
|
|
26329
26720
|
}
|
|
26330
|
-
return new Promise((
|
|
26721
|
+
return new Promise((resolve49) => {
|
|
26331
26722
|
const shutdown = async () => {
|
|
26332
26723
|
if (stopped) return;
|
|
26333
26724
|
stopped = true;
|
|
@@ -26337,7 +26728,7 @@ async function runForeground(root) {
|
|
|
26337
26728
|
process.chdir(priorCwd);
|
|
26338
26729
|
} catch {
|
|
26339
26730
|
}
|
|
26340
|
-
|
|
26731
|
+
resolve49({ exitCode: 0 });
|
|
26341
26732
|
};
|
|
26342
26733
|
process.on("SIGINT", () => {
|
|
26343
26734
|
void shutdown();
|
|
@@ -26398,7 +26789,7 @@ async function runOnQuiescent(projectRoot) {
|
|
|
26398
26789
|
);
|
|
26399
26790
|
}
|
|
26400
26791
|
function refreshLogPath(projectRoot) {
|
|
26401
|
-
return
|
|
26792
|
+
return resolve26(projectRoot, ".massu", "refresh-log.jsonl");
|
|
26402
26793
|
}
|
|
26403
26794
|
function appendRefreshLog(projectRoot, event) {
|
|
26404
26795
|
const path = refreshLogPath(projectRoot);
|
|
@@ -26410,11 +26801,11 @@ function appendRefreshLog(projectRoot, event) {
|
|
|
26410
26801
|
}
|
|
26411
26802
|
function readRefreshLog(projectRoot, limit = 10, opts = {}) {
|
|
26412
26803
|
const path = refreshLogPath(projectRoot);
|
|
26413
|
-
if (!
|
|
26804
|
+
if (!existsSync48(path)) return [];
|
|
26414
26805
|
const warn = opts.warn ?? ((s) => {
|
|
26415
26806
|
process.stderr.write(s);
|
|
26416
26807
|
});
|
|
26417
|
-
const lines =
|
|
26808
|
+
const lines = readFileSync36(path, "utf-8").split("\n").filter(Boolean);
|
|
26418
26809
|
const tail = lines.slice(-limit);
|
|
26419
26810
|
const out = [];
|
|
26420
26811
|
let corrupt = 0;
|
|
@@ -26487,13 +26878,13 @@ var init_refresh_log = __esm({
|
|
|
26487
26878
|
import {
|
|
26488
26879
|
chmodSync as chmodSync9,
|
|
26489
26880
|
closeSync as closeSync6,
|
|
26490
|
-
existsSync as
|
|
26881
|
+
existsSync as existsSync49,
|
|
26491
26882
|
fsyncSync as fsyncSync6,
|
|
26492
26883
|
mkdirSync as mkdirSync24,
|
|
26493
26884
|
openSync as openSync6,
|
|
26494
26885
|
renameSync as renameSync6,
|
|
26495
26886
|
rmSync as rmSync12,
|
|
26496
|
-
statSync as
|
|
26887
|
+
statSync as statSync17,
|
|
26497
26888
|
writeSync as writeSync4
|
|
26498
26889
|
} from "node:fs";
|
|
26499
26890
|
import { dirname as dirname23 } from "node:path";
|
|
@@ -26501,7 +26892,7 @@ function atomicWrite(path, content, opts = {}) {
|
|
|
26501
26892
|
const tmpPath = `${path}.tmp`;
|
|
26502
26893
|
const parentDir = dirname23(path);
|
|
26503
26894
|
try {
|
|
26504
|
-
if (!
|
|
26895
|
+
if (!existsSync49(parentDir)) {
|
|
26505
26896
|
const mkdirOpts = { recursive: true };
|
|
26506
26897
|
if (opts.ensureParentDirMode !== void 0) {
|
|
26507
26898
|
mkdirOpts.mode = opts.ensureParentDirMode;
|
|
@@ -26523,7 +26914,7 @@ function atomicWrite(path, content, opts = {}) {
|
|
|
26523
26914
|
renameSync6(tmpPath, path);
|
|
26524
26915
|
return { written: true };
|
|
26525
26916
|
} catch (err) {
|
|
26526
|
-
if (
|
|
26917
|
+
if (existsSync49(tmpPath)) {
|
|
26527
26918
|
try {
|
|
26528
26919
|
rmSync12(tmpPath, { force: true });
|
|
26529
26920
|
} catch {
|
|
@@ -26539,23 +26930,23 @@ var init_atomic_write = __esm({
|
|
|
26539
26930
|
});
|
|
26540
26931
|
|
|
26541
26932
|
// src/security/manifest-schema.ts
|
|
26542
|
-
import { z as
|
|
26933
|
+
import { z as z5 } from "zod";
|
|
26543
26934
|
var MIN_KNOWN_SCHEMA_VERSION, KNOWN_MAX_SCHEMA_VERSION, Sha256HexSchema, PrintableAsciiStringSchema, Base64Schema, AdapterEntrySchema, ManifestBodySchema, EnvelopeSchema;
|
|
26544
26935
|
var init_manifest_schema = __esm({
|
|
26545
26936
|
"src/security/manifest-schema.ts"() {
|
|
26546
26937
|
"use strict";
|
|
26547
26938
|
MIN_KNOWN_SCHEMA_VERSION = 1;
|
|
26548
26939
|
KNOWN_MAX_SCHEMA_VERSION = 1;
|
|
26549
|
-
Sha256HexSchema =
|
|
26550
|
-
PrintableAsciiStringSchema =
|
|
26940
|
+
Sha256HexSchema = z5.string().regex(/^[0-9a-f]{64}$/, "sha256 hex must be 64 lowercase hex chars");
|
|
26941
|
+
PrintableAsciiStringSchema = z5.string().min(1).regex(
|
|
26551
26942
|
/^[\x20-\x7e]+$/,
|
|
26552
26943
|
"must be printable ASCII (0x20-0x7e); control characters like ESC, tab, newline, and non-ASCII are rejected to prevent log injection"
|
|
26553
26944
|
);
|
|
26554
|
-
Base64Schema =
|
|
26945
|
+
Base64Schema = z5.string().regex(
|
|
26555
26946
|
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/,
|
|
26556
26947
|
"must be standard base64 (RFC 4648; length divisible by 4 with optional == or = padding)"
|
|
26557
26948
|
).min(1);
|
|
26558
|
-
AdapterEntrySchema =
|
|
26949
|
+
AdapterEntrySchema = z5.object({
|
|
26559
26950
|
// CR-9 iter-4 audit LOW-NEW4-3 fix: every string field rendered in CLI
|
|
26560
26951
|
// output (discover.ts deprecation warning + adapters.ts search status)
|
|
26561
26952
|
// uses PrintableAsciiStringSchema to prevent log-injection from a
|
|
@@ -26566,24 +26957,24 @@ var init_manifest_schema = __esm({
|
|
|
26566
26957
|
version: PrintableAsciiStringSchema,
|
|
26567
26958
|
sha256: Sha256HexSchema,
|
|
26568
26959
|
signing_key_id: Sha256HexSchema,
|
|
26569
|
-
deprecated:
|
|
26960
|
+
deprecated: z5.object({
|
|
26570
26961
|
since: PrintableAsciiStringSchema,
|
|
26571
26962
|
replacement: PrintableAsciiStringSchema.nullable().optional(),
|
|
26572
26963
|
reason: PrintableAsciiStringSchema
|
|
26573
26964
|
}).optional(),
|
|
26574
|
-
unpublished:
|
|
26965
|
+
unpublished: z5.boolean().optional()
|
|
26575
26966
|
}).passthrough();
|
|
26576
|
-
ManifestBodySchema =
|
|
26577
|
-
manifest_schema_version:
|
|
26578
|
-
issued_at:
|
|
26579
|
-
adapters:
|
|
26967
|
+
ManifestBodySchema = z5.object({
|
|
26968
|
+
manifest_schema_version: z5.number().int().positive().default(1),
|
|
26969
|
+
issued_at: z5.string().min(1),
|
|
26970
|
+
adapters: z5.array(AdapterEntrySchema)
|
|
26580
26971
|
}).passthrough();
|
|
26581
|
-
EnvelopeSchema =
|
|
26972
|
+
EnvelopeSchema = z5.object({
|
|
26582
26973
|
manifest: ManifestBodySchema,
|
|
26583
26974
|
manifest_b64: Base64Schema,
|
|
26584
26975
|
signature: Base64Schema,
|
|
26585
26976
|
manifest_sha256: Sha256HexSchema,
|
|
26586
|
-
signed_at:
|
|
26977
|
+
signed_at: z5.string().min(1),
|
|
26587
26978
|
signing_key_id: Sha256HexSchema
|
|
26588
26979
|
}).passthrough();
|
|
26589
26980
|
}
|
|
@@ -26872,24 +27263,24 @@ var init_fetcher = __esm({
|
|
|
26872
27263
|
});
|
|
26873
27264
|
|
|
26874
27265
|
// src/security/manifest-cache.ts
|
|
26875
|
-
import { existsSync as
|
|
27266
|
+
import { existsSync as existsSync50, readFileSync as readFileSync37, statSync as statSync18 } from "node:fs";
|
|
26876
27267
|
import { homedir as homedir21 } from "node:os";
|
|
26877
|
-
import { resolve as
|
|
26878
|
-
import { z as
|
|
27268
|
+
import { resolve as resolve27 } from "node:path";
|
|
27269
|
+
import { z as z6 } from "zod";
|
|
26879
27270
|
function defaultCachePaths() {
|
|
26880
|
-
const dir =
|
|
27271
|
+
const dir = resolve27(homedir21(), ".massu");
|
|
26881
27272
|
return {
|
|
26882
|
-
cachePath:
|
|
26883
|
-
lockPath:
|
|
27273
|
+
cachePath: resolve27(dir, "adapter-manifest.json"),
|
|
27274
|
+
lockPath: resolve27(dir, ".adapter-manifest.lock")
|
|
26884
27275
|
};
|
|
26885
27276
|
}
|
|
26886
27277
|
function loadCachedManifest(paths = defaultCachePaths()) {
|
|
26887
|
-
if (!
|
|
27278
|
+
if (!existsSync50(paths.cachePath)) {
|
|
26888
27279
|
return { kind: "absent" };
|
|
26889
27280
|
}
|
|
26890
27281
|
let raw;
|
|
26891
27282
|
try {
|
|
26892
|
-
const content =
|
|
27283
|
+
const content = readFileSync37(paths.cachePath, "utf-8");
|
|
26893
27284
|
raw = JSON.parse(content);
|
|
26894
27285
|
} catch (err) {
|
|
26895
27286
|
return {
|
|
@@ -27036,7 +27427,7 @@ var init_manifest_cache = __esm({
|
|
|
27036
27427
|
MAX_FRESH_MS = 24 * 60 * 60 * 1e3;
|
|
27037
27428
|
MAX_STALE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
27038
27429
|
REGISTRY_MANIFEST_URL = "https://registry.massu.ai/adapters/manifest.json";
|
|
27039
|
-
CacheWrapperSchema =
|
|
27430
|
+
CacheWrapperSchema = z6.object({
|
|
27040
27431
|
envelope: EnvelopeSchema,
|
|
27041
27432
|
// CR-9 iter-6 audit LOW-NEW6-1 fix: fetched_at flows into the
|
|
27042
27433
|
// 'fetched_at not parseable as Date' reason render at line 163 → wrapped
|
|
@@ -27047,7 +27438,7 @@ var init_manifest_cache = __esm({
|
|
|
27047
27438
|
// drift-guard test (test_security_schemas_printable_ascii_drift.test.ts)
|
|
27048
27439
|
// makes this class of bug structurally impossible to recur.
|
|
27049
27440
|
fetched_at: PrintableAsciiStringSchema,
|
|
27050
|
-
bundled_pubkey_fingerprint:
|
|
27441
|
+
bundled_pubkey_fingerprint: z6.string().regex(/^[0-9a-f]{64}$/)
|
|
27051
27442
|
}).strict();
|
|
27052
27443
|
}
|
|
27053
27444
|
});
|
|
@@ -27078,15 +27469,15 @@ var init_adapter_origin = __esm({
|
|
|
27078
27469
|
});
|
|
27079
27470
|
|
|
27080
27471
|
// src/security/local-fingerprint.ts
|
|
27081
|
-
import { existsSync as
|
|
27472
|
+
import { existsSync as existsSync51, readFileSync as readFileSync38, lstatSync as lstatSync5 } from "node:fs";
|
|
27082
27473
|
import { homedir as homedir22 } from "node:os";
|
|
27083
|
-
import { resolve as
|
|
27474
|
+
import { resolve as resolve28, isAbsolute as isAbsolute4 } from "node:path";
|
|
27084
27475
|
import { createHash as createHash14 } from "node:crypto";
|
|
27085
|
-
import { z as
|
|
27476
|
+
import { z as z7 } from "zod";
|
|
27086
27477
|
function computeLocalFingerprint(localPaths, projectRoot) {
|
|
27087
27478
|
const tuples = [];
|
|
27088
27479
|
for (const p19 of localPaths) {
|
|
27089
|
-
const abs = isAbsolute4(p19) ? p19 :
|
|
27480
|
+
const abs = isAbsolute4(p19) ? p19 : resolve28(projectRoot, p19);
|
|
27090
27481
|
let contentTag;
|
|
27091
27482
|
try {
|
|
27092
27483
|
const lst = lstatSync5(abs);
|
|
@@ -27095,7 +27486,7 @@ function computeLocalFingerprint(localPaths, projectRoot) {
|
|
|
27095
27486
|
} else if (!lst.isFile()) {
|
|
27096
27487
|
contentTag = "<not-a-file>";
|
|
27097
27488
|
} else {
|
|
27098
|
-
contentTag = createHash14("sha256").update(
|
|
27489
|
+
contentTag = createHash14("sha256").update(readFileSync38(abs)).digest("hex");
|
|
27099
27490
|
}
|
|
27100
27491
|
} catch {
|
|
27101
27492
|
contentTag = "<missing>";
|
|
@@ -27107,10 +27498,10 @@ function computeLocalFingerprint(localPaths, projectRoot) {
|
|
|
27107
27498
|
return createHash14("sha256").update(canonical2).digest("hex");
|
|
27108
27499
|
}
|
|
27109
27500
|
function readFingerprintSentinel(path = FINGERPRINT_PATH) {
|
|
27110
|
-
if (!
|
|
27501
|
+
if (!existsSync51(path)) return null;
|
|
27111
27502
|
let raw;
|
|
27112
27503
|
try {
|
|
27113
|
-
raw = JSON.parse(
|
|
27504
|
+
raw = JSON.parse(readFileSync38(path, "utf-8"));
|
|
27114
27505
|
} catch {
|
|
27115
27506
|
return null;
|
|
27116
27507
|
}
|
|
@@ -27158,10 +27549,10 @@ var init_local_fingerprint = __esm({
|
|
|
27158
27549
|
"use strict";
|
|
27159
27550
|
init_atomic_write();
|
|
27160
27551
|
init_manifest_schema();
|
|
27161
|
-
FINGERPRINT_PATH =
|
|
27162
|
-
FingerprintSentinelSchema =
|
|
27163
|
-
fingerprint:
|
|
27164
|
-
source:
|
|
27552
|
+
FINGERPRINT_PATH = resolve28(homedir22(), ".massu", "adapters-local-fingerprint.json");
|
|
27553
|
+
FingerprintSentinelSchema = z7.object({
|
|
27554
|
+
fingerprint: z7.string().regex(/^[0-9a-f]{64}$/),
|
|
27555
|
+
source: z7.enum(["cli", "cli-resync"]),
|
|
27165
27556
|
// CR-9 iter-5 audit LOW-NEW5-1 fix: ts is rendered to stderr in the
|
|
27166
27557
|
// drift warning at line 214 (`${sentinel.source} at ${sentinel.ts}`);
|
|
27167
27558
|
// a same-user attacker writing the sentinel file with control chars
|
|
@@ -27174,15 +27565,15 @@ var init_local_fingerprint = __esm({
|
|
|
27174
27565
|
});
|
|
27175
27566
|
|
|
27176
27567
|
// src/security/install-tracking.ts
|
|
27177
|
-
import { readFileSync as
|
|
27568
|
+
import { readFileSync as readFileSync39, readdirSync as readdirSync18, lstatSync as lstatSync6, existsSync as existsSync52 } from "node:fs";
|
|
27178
27569
|
import { join as join28, relative as relative8, sep as sep3 } from "node:path";
|
|
27179
27570
|
import { homedir as homedir23 } from "node:os";
|
|
27180
|
-
import { resolve as
|
|
27571
|
+
import { resolve as resolve29 } from "node:path";
|
|
27181
27572
|
import { createHash as createHash15 } from "node:crypto";
|
|
27182
|
-
import { z as
|
|
27573
|
+
import { z as z8 } from "zod";
|
|
27183
27574
|
function containsHiddenDirs(packageDir) {
|
|
27184
27575
|
for (const hidden of EXCLUDED_DIR_NAMES) {
|
|
27185
|
-
if (
|
|
27576
|
+
if (existsSync52(`${packageDir}/${hidden}`)) {
|
|
27186
27577
|
return hidden;
|
|
27187
27578
|
}
|
|
27188
27579
|
}
|
|
@@ -27194,7 +27585,7 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
27194
27585
|
function walk(currentDir) {
|
|
27195
27586
|
let entries;
|
|
27196
27587
|
try {
|
|
27197
|
-
entries =
|
|
27588
|
+
entries = readdirSync18(currentDir);
|
|
27198
27589
|
} catch {
|
|
27199
27590
|
return;
|
|
27200
27591
|
}
|
|
@@ -27228,7 +27619,7 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
27228
27619
|
files.sort((a2, b2) => a2.relativePath < b2.relativePath ? -1 : a2.relativePath > b2.relativePath ? 1 : 0);
|
|
27229
27620
|
const top = createHash15("sha256");
|
|
27230
27621
|
for (const f2 of files) {
|
|
27231
|
-
const fileHash = createHash15("sha256").update(
|
|
27622
|
+
const fileHash = createHash15("sha256").update(readFileSync39(f2.absPath)).digest("hex");
|
|
27232
27623
|
top.update(f2.relativePath, "utf-8");
|
|
27233
27624
|
top.update("\0", "utf-8");
|
|
27234
27625
|
top.update(fileHash, "utf-8");
|
|
@@ -27237,10 +27628,10 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
27237
27628
|
return top.digest("hex");
|
|
27238
27629
|
}
|
|
27239
27630
|
function readInstalledManifest(path = INSTALLED_MANIFEST_PATH) {
|
|
27240
|
-
if (!
|
|
27631
|
+
if (!existsSync52(path)) return {};
|
|
27241
27632
|
let raw;
|
|
27242
27633
|
try {
|
|
27243
|
-
raw = JSON.parse(
|
|
27634
|
+
raw = JSON.parse(readFileSync39(path, "utf-8"));
|
|
27244
27635
|
} catch {
|
|
27245
27636
|
return {};
|
|
27246
27637
|
}
|
|
@@ -27304,10 +27695,10 @@ var init_install_tracking = __esm({
|
|
|
27304
27695
|
"src/security/install-tracking.ts"() {
|
|
27305
27696
|
"use strict";
|
|
27306
27697
|
init_atomic_write();
|
|
27307
|
-
INSTALLED_MANIFEST_PATH =
|
|
27698
|
+
INSTALLED_MANIFEST_PATH = resolve29(homedir23(), ".massu", "adapter-manifest-installed.json");
|
|
27308
27699
|
DEFAULT_MAX_FILE_BYTES = 64 * 1024 * 1024;
|
|
27309
27700
|
EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", ".cache", ".tmp"]);
|
|
27310
|
-
InstallEntrySchema =
|
|
27701
|
+
InstallEntrySchema = z8.object({
|
|
27311
27702
|
// CR-9 iter-3 audit LOW-NEW3-1 fix: reject control characters in version.
|
|
27312
27703
|
// Without this, a same-user attacker writing the install-tracking
|
|
27313
27704
|
// sidecar could embed ANSI escapes (or other control chars) in version,
|
|
@@ -27317,35 +27708,35 @@ var init_install_tracking = __esm({
|
|
|
27317
27708
|
// rejected at parse time. Schema-level validation closes the vector
|
|
27318
27709
|
// at every callsite that reads the sidecar — no per-callsite escaping
|
|
27319
27710
|
// gymnastics needed.
|
|
27320
|
-
version:
|
|
27321
|
-
installed_sha256:
|
|
27322
|
-
manifest_sha256:
|
|
27323
|
-
ts:
|
|
27711
|
+
version: z8.string().min(1).regex(/^[\x20-\x7e]+$/, "version must be printable ASCII (no control characters)"),
|
|
27712
|
+
installed_sha256: z8.string().regex(/^[0-9a-f]{64}$/),
|
|
27713
|
+
manifest_sha256: z8.string().regex(/^[0-9a-f]{64}$/),
|
|
27714
|
+
ts: z8.string().min(1).regex(/^[\x20-\x7e]+$/, "ts must be printable ASCII (no control characters)")
|
|
27324
27715
|
}).strict();
|
|
27325
|
-
InstalledManifestSchema =
|
|
27716
|
+
InstalledManifestSchema = z8.record(z8.string(), InstallEntrySchema);
|
|
27326
27717
|
}
|
|
27327
27718
|
});
|
|
27328
27719
|
|
|
27329
27720
|
// src/detect/adapters/discover.ts
|
|
27330
|
-
import { existsSync as
|
|
27331
|
-
import { resolve as
|
|
27332
|
-
import { z as
|
|
27721
|
+
import { existsSync as existsSync53, readdirSync as readdirSync19, readFileSync as readFileSync40, lstatSync as lstatSync7 } from "node:fs";
|
|
27722
|
+
import { resolve as resolve30, isAbsolute as isAbsolute5 } from "node:path";
|
|
27723
|
+
import { z as z9 } from "zod";
|
|
27333
27724
|
function walkNodeModules(projectRoot, warnings) {
|
|
27334
|
-
const nodeModulesDir =
|
|
27335
|
-
if (!
|
|
27725
|
+
const nodeModulesDir = resolve30(projectRoot, "node_modules");
|
|
27726
|
+
if (!existsSync53(nodeModulesDir)) {
|
|
27336
27727
|
return [];
|
|
27337
27728
|
}
|
|
27338
27729
|
const candidates = [];
|
|
27339
27730
|
let topLevelEntries;
|
|
27340
27731
|
try {
|
|
27341
|
-
topLevelEntries =
|
|
27732
|
+
topLevelEntries = readdirSync19(nodeModulesDir);
|
|
27342
27733
|
} catch (err) {
|
|
27343
27734
|
warnings.push(`failed to read node_modules: ${err instanceof Error ? err.message : String(err)}`);
|
|
27344
27735
|
return [];
|
|
27345
27736
|
}
|
|
27346
27737
|
for (const entry of topLevelEntries) {
|
|
27347
27738
|
if (entry.startsWith(".")) continue;
|
|
27348
|
-
const entryPath =
|
|
27739
|
+
const entryPath = resolve30(nodeModulesDir, entry);
|
|
27349
27740
|
let entryStat;
|
|
27350
27741
|
try {
|
|
27351
27742
|
entryStat = lstatSync7(entryPath);
|
|
@@ -27362,12 +27753,12 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
27362
27753
|
if (entry.startsWith("@")) {
|
|
27363
27754
|
let scopedEntries;
|
|
27364
27755
|
try {
|
|
27365
|
-
scopedEntries =
|
|
27756
|
+
scopedEntries = readdirSync19(entryPath);
|
|
27366
27757
|
} catch {
|
|
27367
27758
|
continue;
|
|
27368
27759
|
}
|
|
27369
27760
|
for (const sub of scopedEntries) {
|
|
27370
|
-
const subPath =
|
|
27761
|
+
const subPath = resolve30(entryPath, sub);
|
|
27371
27762
|
let subStat;
|
|
27372
27763
|
try {
|
|
27373
27764
|
subStat = lstatSync7(subPath);
|
|
@@ -27386,11 +27777,11 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
27386
27777
|
return candidates;
|
|
27387
27778
|
}
|
|
27388
27779
|
function tryReadAdapterPackage(packageDir, warnings) {
|
|
27389
|
-
const pkgJsonPath =
|
|
27390
|
-
if (!
|
|
27780
|
+
const pkgJsonPath = resolve30(packageDir, "package.json");
|
|
27781
|
+
if (!existsSync53(pkgJsonPath)) return null;
|
|
27391
27782
|
let raw;
|
|
27392
27783
|
try {
|
|
27393
|
-
raw = JSON.parse(
|
|
27784
|
+
raw = JSON.parse(readFileSync40(pkgJsonPath, "utf-8"));
|
|
27394
27785
|
} catch (err) {
|
|
27395
27786
|
warnings.push(
|
|
27396
27787
|
`skipping ${packageDir}: package.json parse failed (${err instanceof Error ? err.message : String(err)})`
|
|
@@ -27524,8 +27915,8 @@ function discoverAdapters(opts) {
|
|
|
27524
27915
|
const localSet = new Set(opts.configLocalPaths);
|
|
27525
27916
|
for (const localPath of opts.configLocalPaths) {
|
|
27526
27917
|
if (seenIds.has(localPath)) continue;
|
|
27527
|
-
const absPath = isAbsolute5(localPath) ? localPath :
|
|
27528
|
-
if (!
|
|
27918
|
+
const absPath = isAbsolute5(localPath) ? localPath : resolve30(opts.projectRoot, localPath);
|
|
27919
|
+
if (!existsSync53(absPath)) {
|
|
27529
27920
|
warnings.push(
|
|
27530
27921
|
`local adapter file not found: ${localPath} (resolved to ${absPath}). Remove via: massu adapters remove-local ${localPath}`
|
|
27531
27922
|
);
|
|
@@ -27556,7 +27947,7 @@ var init_discover = __esm({
|
|
|
27556
27947
|
init_manifest_schema();
|
|
27557
27948
|
init_local_fingerprint();
|
|
27558
27949
|
init_install_tracking();
|
|
27559
|
-
AdapterPackageJsonSchema =
|
|
27950
|
+
AdapterPackageJsonSchema = z9.object({
|
|
27560
27951
|
// CR-9 iter-4 audit LOW-NEW4-2 fix: name/version are rendered in stderr
|
|
27561
27952
|
// warnings via `${pkg.name}@${pkg.version}` strings; without the
|
|
27562
27953
|
// printable-ASCII regex, a malicious local node_modules/<pkg>/package.json
|
|
@@ -27566,9 +27957,9 @@ var init_discover = __esm({
|
|
|
27566
27957
|
version: PrintableAsciiStringSchema,
|
|
27567
27958
|
// Plan 3c gap-31 + gap-50: `"massu-adapter": true` is the explicit opt-in
|
|
27568
27959
|
// marker. @massu/adapter-* packages also declare this.
|
|
27569
|
-
"massu-adapter":
|
|
27960
|
+
"massu-adapter": z9.union([z9.boolean(), z9.literal(void 0)]).optional(),
|
|
27570
27961
|
// Plan 3c gap-31: api version. Loader refuses incompatible major (caller-side).
|
|
27571
|
-
"massu-adapter-api-version":
|
|
27962
|
+
"massu-adapter-api-version": z9.union([z9.string(), z9.number(), z9.literal(void 0)]).optional()
|
|
27572
27963
|
}).passthrough();
|
|
27573
27964
|
}
|
|
27574
27965
|
});
|
|
@@ -27607,8 +27998,8 @@ __export(adapters_exports, {
|
|
|
27607
27998
|
runAdaptersResyncLocalFingerprint: () => runAdaptersResyncLocalFingerprint,
|
|
27608
27999
|
runAdaptersSearch: () => runAdaptersSearch
|
|
27609
28000
|
});
|
|
27610
|
-
import { existsSync as
|
|
27611
|
-
import { resolve as
|
|
28001
|
+
import { existsSync as existsSync54, readFileSync as readFileSync41 } from "node:fs";
|
|
28002
|
+
import { resolve as resolve31 } from "node:path";
|
|
27612
28003
|
import { parseDocument } from "yaml";
|
|
27613
28004
|
async function handleAdaptersSubcommand(args2) {
|
|
27614
28005
|
const sub = args2[0];
|
|
@@ -27875,8 +28266,8 @@ function mutateLocalArray(mutator, command) {
|
|
|
27875
28266
|
);
|
|
27876
28267
|
return { exitCode: 2 };
|
|
27877
28268
|
}
|
|
27878
|
-
const yamlPath =
|
|
27879
|
-
if (!
|
|
28269
|
+
const yamlPath = resolve31(projectRoot, "massu.config.yaml");
|
|
28270
|
+
if (!existsSync54(yamlPath)) {
|
|
27880
28271
|
process.stderr.write(
|
|
27881
28272
|
`${command}: massu.config.yaml not found at ${yamlPath}. Run \`massu init\` first.
|
|
27882
28273
|
`
|
|
@@ -27885,7 +28276,7 @@ function mutateLocalArray(mutator, command) {
|
|
|
27885
28276
|
}
|
|
27886
28277
|
let yamlText;
|
|
27887
28278
|
try {
|
|
27888
|
-
yamlText =
|
|
28279
|
+
yamlText = readFileSync41(yamlPath, "utf-8");
|
|
27889
28280
|
} catch (err) {
|
|
27890
28281
|
process.stderr.write(`${command}: failed to read ${yamlPath}: ${err instanceof Error ? err.message : String(err)}
|
|
27891
28282
|
`);
|
|
@@ -27906,7 +28297,7 @@ function mutateLocalArray(mutator, command) {
|
|
|
27906
28297
|
if (next === null) {
|
|
27907
28298
|
return { exitCode: 0 };
|
|
27908
28299
|
}
|
|
27909
|
-
const lockPath =
|
|
28300
|
+
const lockPath = resolve31(projectRoot, ".massu", "adapters-local-mutate.lock");
|
|
27910
28301
|
return withFileLockSync(lockPath, () => {
|
|
27911
28302
|
doc.setIn(["adapters", "local"], next);
|
|
27912
28303
|
const newYaml = doc.toString();
|
|
@@ -27975,23 +28366,23 @@ async function runAdaptersInstall(args2) {
|
|
|
27975
28366
|
`);
|
|
27976
28367
|
return { exitCode: 1 };
|
|
27977
28368
|
}
|
|
27978
|
-
const packageDir =
|
|
27979
|
-
if (!
|
|
28369
|
+
const packageDir = resolve31(projectRoot, "node_modules", ...packageName.split("/"));
|
|
28370
|
+
if (!existsSync54(packageDir)) {
|
|
27980
28371
|
process.stderr.write(
|
|
27981
28372
|
`install: ${packageName} is not installed in node_modules. Run \`npm install ${packageName}\` first.
|
|
27982
28373
|
`
|
|
27983
28374
|
);
|
|
27984
28375
|
return { exitCode: 1 };
|
|
27985
28376
|
}
|
|
27986
|
-
const pkgJsonPath =
|
|
27987
|
-
if (!
|
|
28377
|
+
const pkgJsonPath = resolve31(packageDir, "package.json");
|
|
28378
|
+
if (!existsSync54(pkgJsonPath)) {
|
|
27988
28379
|
process.stderr.write(`install: ${packageName} has no package.json at ${pkgJsonPath}
|
|
27989
28380
|
`);
|
|
27990
28381
|
return { exitCode: 1 };
|
|
27991
28382
|
}
|
|
27992
28383
|
let pkgJson;
|
|
27993
28384
|
try {
|
|
27994
|
-
pkgJson = JSON.parse(
|
|
28385
|
+
pkgJson = JSON.parse(readFileSync41(pkgJsonPath, "utf-8"));
|
|
27995
28386
|
} catch (err) {
|
|
27996
28387
|
process.stderr.write(`install: ${packageName} has malformed package.json: ${err instanceof Error ? err.message : String(err)}
|
|
27997
28388
|
`);
|
|
@@ -28110,8 +28501,8 @@ async function runAdaptersResign(_args) {
|
|
|
28110
28501
|
removeInstalledManifestEntry(name);
|
|
28111
28502
|
continue;
|
|
28112
28503
|
}
|
|
28113
|
-
const packageDir =
|
|
28114
|
-
if (!
|
|
28504
|
+
const packageDir = resolve31(projectRoot, "node_modules", ...name.split("/"));
|
|
28505
|
+
if (!existsSync54(packageDir)) {
|
|
28115
28506
|
removed++;
|
|
28116
28507
|
warnings.push(`${name}@${entry.version}: not present in node_modules \u2014 REMOVED from sidecar`);
|
|
28117
28508
|
removeInstalledManifestEntry(name);
|
|
@@ -28209,7 +28600,7 @@ var init_sql_table_names = __esm({
|
|
|
28209
28600
|
|
|
28210
28601
|
// src/db.ts
|
|
28211
28602
|
import { dirname as dirname24, join as join29 } from "path";
|
|
28212
|
-
import { existsSync as
|
|
28603
|
+
import { existsSync as existsSync55, mkdirSync as mkdirSync25, readdirSync as readdirSync20, statSync as statSync19 } from "fs";
|
|
28213
28604
|
function getCodeGraphDb() {
|
|
28214
28605
|
const dbPath = getResolvedPaths().codegraphDbPath;
|
|
28215
28606
|
assertCodegraphUsable(dbPath);
|
|
@@ -28220,7 +28611,7 @@ function getCodeGraphDb() {
|
|
|
28220
28611
|
function getDataDb() {
|
|
28221
28612
|
const dbPath = getResolvedPaths().dataDbPath;
|
|
28222
28613
|
const dir = dirname24(dbPath);
|
|
28223
|
-
if (!
|
|
28614
|
+
if (!existsSync55(dir)) {
|
|
28224
28615
|
mkdirSync25(dir, { recursive: true });
|
|
28225
28616
|
}
|
|
28226
28617
|
const db = openDatabase2(dbPath);
|
|
@@ -28502,14 +28893,14 @@ function isPythonDataStale(dataDb, pythonRoot) {
|
|
|
28502
28893
|
const lastBuildTime = new Date(lastBuild.value).getTime();
|
|
28503
28894
|
function checkDir(dir) {
|
|
28504
28895
|
try {
|
|
28505
|
-
const entries =
|
|
28896
|
+
const entries = readdirSync20(dir, { withFileTypes: true });
|
|
28506
28897
|
for (const entry of entries) {
|
|
28507
28898
|
const fullPath = join29(dir, entry.name);
|
|
28508
28899
|
if (entry.isDirectory()) {
|
|
28509
28900
|
if (["__pycache__", ".venv", "venv", "node_modules", ".mypy_cache", ".pytest_cache"].includes(entry.name)) continue;
|
|
28510
28901
|
if (checkDir(fullPath)) return true;
|
|
28511
28902
|
} else if (entry.name.endsWith(".py")) {
|
|
28512
|
-
if (
|
|
28903
|
+
if (statSync19(fullPath).mtimeMs > lastBuildTime) return true;
|
|
28513
28904
|
}
|
|
28514
28905
|
}
|
|
28515
28906
|
} catch {
|
|
@@ -28536,10 +28927,10 @@ var init_db2 = __esm({
|
|
|
28536
28927
|
});
|
|
28537
28928
|
|
|
28538
28929
|
// src/security-utils.ts
|
|
28539
|
-
import { resolve as
|
|
28930
|
+
import { resolve as resolve32, normalize } from "path";
|
|
28540
28931
|
function ensureWithinRoot(filePath, projectRoot) {
|
|
28541
|
-
const resolvedRoot =
|
|
28542
|
-
const resolvedPath =
|
|
28932
|
+
const resolvedRoot = resolve32(projectRoot);
|
|
28933
|
+
const resolvedPath = resolve32(resolvedRoot, filePath);
|
|
28543
28934
|
const normalizedPath = normalize(resolvedPath);
|
|
28544
28935
|
const normalizedRoot = normalize(resolvedRoot);
|
|
28545
28936
|
if (!normalizedPath.startsWith(normalizedRoot + "/") && normalizedPath !== normalizedRoot) {
|
|
@@ -28728,8 +29119,8 @@ var init_source_layout = __esm({
|
|
|
28728
29119
|
});
|
|
28729
29120
|
|
|
28730
29121
|
// src/import-resolver.ts
|
|
28731
|
-
import { readFileSync as
|
|
28732
|
-
import { resolve as
|
|
29122
|
+
import { readFileSync as readFileSync42, existsSync as existsSync56, statSync as statSync20 } from "fs";
|
|
29123
|
+
import { resolve as resolve33, dirname as dirname25, join as join30 } from "path";
|
|
28733
29124
|
function parseImports(source) {
|
|
28734
29125
|
const imports = [];
|
|
28735
29126
|
const lines = source.split("\n");
|
|
@@ -28785,23 +29176,23 @@ function resolveImportPath(specifier, fromFile) {
|
|
|
28785
29176
|
let basePath;
|
|
28786
29177
|
if (specifier.startsWith("@/")) {
|
|
28787
29178
|
const paths = getResolvedPaths();
|
|
28788
|
-
basePath =
|
|
29179
|
+
basePath = resolve33(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
|
|
28789
29180
|
} else {
|
|
28790
|
-
basePath =
|
|
29181
|
+
basePath = resolve33(dirname25(fromFile), specifier);
|
|
28791
29182
|
}
|
|
28792
|
-
if (
|
|
29183
|
+
if (existsSync56(basePath) && !isDirectory(basePath)) {
|
|
28793
29184
|
return toRelative2(basePath);
|
|
28794
29185
|
}
|
|
28795
29186
|
const resolvedPaths = getResolvedPaths();
|
|
28796
29187
|
for (const ext of resolvedPaths.extensions) {
|
|
28797
29188
|
const withExt = basePath + ext;
|
|
28798
|
-
if (
|
|
29189
|
+
if (existsSync56(withExt)) {
|
|
28799
29190
|
return toRelative2(withExt);
|
|
28800
29191
|
}
|
|
28801
29192
|
}
|
|
28802
29193
|
for (const indexFile of resolvedPaths.indexFiles) {
|
|
28803
29194
|
const indexPath = join30(basePath, indexFile);
|
|
28804
|
-
if (
|
|
29195
|
+
if (existsSync56(indexPath)) {
|
|
28805
29196
|
return toRelative2(indexPath);
|
|
28806
29197
|
}
|
|
28807
29198
|
}
|
|
@@ -28809,7 +29200,7 @@ function resolveImportPath(specifier, fromFile) {
|
|
|
28809
29200
|
}
|
|
28810
29201
|
function isDirectory(path) {
|
|
28811
29202
|
try {
|
|
28812
|
-
return
|
|
29203
|
+
return statSync20(path).isDirectory();
|
|
28813
29204
|
} catch {
|
|
28814
29205
|
return false;
|
|
28815
29206
|
}
|
|
@@ -28845,11 +29236,11 @@ function buildImportIndex(dataDb, codegraphDb) {
|
|
|
28845
29236
|
const batchSize = 500;
|
|
28846
29237
|
let batch = [];
|
|
28847
29238
|
for (const file of files) {
|
|
28848
|
-
const absPath = ensureWithinRoot(
|
|
28849
|
-
if (!
|
|
29239
|
+
const absPath = ensureWithinRoot(resolve33(projectRoot, file.path), projectRoot);
|
|
29240
|
+
if (!existsSync56(absPath)) continue;
|
|
28850
29241
|
let source;
|
|
28851
29242
|
try {
|
|
28852
|
-
source =
|
|
29243
|
+
source = readFileSync42(absPath, "utf-8");
|
|
28853
29244
|
} catch {
|
|
28854
29245
|
continue;
|
|
28855
29246
|
}
|
|
@@ -28887,15 +29278,15 @@ var init_import_resolver = __esm({
|
|
|
28887
29278
|
});
|
|
28888
29279
|
|
|
28889
29280
|
// src/trpc-index.ts
|
|
28890
|
-
import { readFileSync as
|
|
28891
|
-
import { resolve as
|
|
29281
|
+
import { readFileSync as readFileSync43, existsSync as existsSync57, readdirSync as readdirSync21 } from "fs";
|
|
29282
|
+
import { resolve as resolve34, join as join31 } from "path";
|
|
28892
29283
|
function parseRootRouter() {
|
|
28893
29284
|
const paths = getResolvedPaths();
|
|
28894
29285
|
const rootPath = paths.rootRouterPath;
|
|
28895
|
-
if (!
|
|
29286
|
+
if (!existsSync57(rootPath)) {
|
|
28896
29287
|
throw new Error(`Root router not found at ${rootPath}`);
|
|
28897
29288
|
}
|
|
28898
|
-
const source =
|
|
29289
|
+
const source = readFileSync43(rootPath, "utf-8");
|
|
28899
29290
|
const mappings = [];
|
|
28900
29291
|
const importMap = /* @__PURE__ */ new Map();
|
|
28901
29292
|
const importRegex = /import\s+\{[^}]*?(\w+Router)[^}]*\}\s+from\s+['"]\.\/routers\/([^'"]+)['"]/g;
|
|
@@ -28903,16 +29294,16 @@ function parseRootRouter() {
|
|
|
28903
29294
|
while ((match = importRegex.exec(source)) !== null) {
|
|
28904
29295
|
const variable = match[1];
|
|
28905
29296
|
let filePath = match[2];
|
|
28906
|
-
const fullPath =
|
|
29297
|
+
const fullPath = resolve34(paths.routersDir, filePath);
|
|
28907
29298
|
for (const ext of [".ts", ".tsx", ""]) {
|
|
28908
29299
|
const candidate = fullPath + ext;
|
|
28909
29300
|
const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
|
|
28910
|
-
if (
|
|
29301
|
+
if (existsSync57(candidate)) {
|
|
28911
29302
|
filePath = routersRelPath + "/" + filePath + ext;
|
|
28912
29303
|
break;
|
|
28913
29304
|
}
|
|
28914
29305
|
const indexCandidate = join31(fullPath, "index.ts");
|
|
28915
|
-
if (
|
|
29306
|
+
if (existsSync57(indexCandidate)) {
|
|
28916
29307
|
filePath = routersRelPath + "/" + filePath + "/index.ts";
|
|
28917
29308
|
break;
|
|
28918
29309
|
}
|
|
@@ -28931,9 +29322,9 @@ function parseRootRouter() {
|
|
|
28931
29322
|
return mappings;
|
|
28932
29323
|
}
|
|
28933
29324
|
function extractProcedures(routerFilePath) {
|
|
28934
|
-
const absPath =
|
|
28935
|
-
if (!
|
|
28936
|
-
const source =
|
|
29325
|
+
const absPath = resolve34(getProjectRoot(), routerFilePath);
|
|
29326
|
+
if (!existsSync57(absPath)) return [];
|
|
29327
|
+
const source = readFileSync43(absPath, "utf-8");
|
|
28937
29328
|
const procedures = [];
|
|
28938
29329
|
const seen = /* @__PURE__ */ new Set();
|
|
28939
29330
|
const procRegex = /(\w+)\s*:\s*(protected|public)Procedure/g;
|
|
@@ -28956,19 +29347,19 @@ function findUICallSites(routerKey, procedureName) {
|
|
|
28956
29347
|
const root = getProjectRoot();
|
|
28957
29348
|
const src = config.paths.source;
|
|
28958
29349
|
const searchDirs = [
|
|
28959
|
-
|
|
28960
|
-
|
|
28961
|
-
|
|
29350
|
+
resolve34(root, config.paths.pages ?? src + "/app"),
|
|
29351
|
+
resolve34(root, config.paths.components ?? src + "/components"),
|
|
29352
|
+
resolve34(root, config.paths.hooks ?? src + "/hooks")
|
|
28962
29353
|
];
|
|
28963
29354
|
const searchPattern = `api.${routerKey}.${procedureName}`;
|
|
28964
29355
|
for (const dir of searchDirs) {
|
|
28965
|
-
if (!
|
|
29356
|
+
if (!existsSync57(dir)) continue;
|
|
28966
29357
|
searchDirectory(dir, searchPattern, callSites);
|
|
28967
29358
|
}
|
|
28968
29359
|
return callSites;
|
|
28969
29360
|
}
|
|
28970
29361
|
function searchDirectory(dir, pattern, results) {
|
|
28971
|
-
const entries =
|
|
29362
|
+
const entries = readdirSync21(dir, { withFileTypes: true });
|
|
28972
29363
|
for (const entry of entries) {
|
|
28973
29364
|
const fullPath = join31(dir, entry.name);
|
|
28974
29365
|
if (entry.isDirectory()) {
|
|
@@ -28976,7 +29367,7 @@ function searchDirectory(dir, pattern, results) {
|
|
|
28976
29367
|
searchDirectory(fullPath, pattern, results);
|
|
28977
29368
|
} else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
|
|
28978
29369
|
try {
|
|
28979
|
-
const source =
|
|
29370
|
+
const source = readFileSync43(fullPath, "utf-8");
|
|
28980
29371
|
const lines = source.split("\n");
|
|
28981
29372
|
for (let i = 0; i < lines.length; i++) {
|
|
28982
29373
|
if (lines[i].includes(pattern)) {
|
|
@@ -29040,8 +29431,8 @@ var init_trpc_index = __esm({
|
|
|
29040
29431
|
});
|
|
29041
29432
|
|
|
29042
29433
|
// src/page-deps.ts
|
|
29043
|
-
import { readFileSync as
|
|
29044
|
-
import { resolve as
|
|
29434
|
+
import { readFileSync as readFileSync44, existsSync as existsSync58 } from "fs";
|
|
29435
|
+
import { resolve as resolve35 } from "path";
|
|
29045
29436
|
function deriveRoute(pageFile) {
|
|
29046
29437
|
const pagesDir = getSourceLayout().pagesDir;
|
|
29047
29438
|
const prefix3 = pagesDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -29081,10 +29472,10 @@ function findRouterCalls(files) {
|
|
|
29081
29472
|
const routers = /* @__PURE__ */ new Set();
|
|
29082
29473
|
const projectRoot = getProjectRoot();
|
|
29083
29474
|
for (const file of files) {
|
|
29084
|
-
const absPath = ensureWithinRoot(
|
|
29085
|
-
if (!
|
|
29475
|
+
const absPath = ensureWithinRoot(resolve35(projectRoot, file), projectRoot);
|
|
29476
|
+
if (!existsSync58(absPath)) continue;
|
|
29086
29477
|
try {
|
|
29087
|
-
const source =
|
|
29478
|
+
const source = readFileSync44(absPath, "utf-8");
|
|
29088
29479
|
const apiCallRegex = /api\.(\w+)\.\w+/g;
|
|
29089
29480
|
let match;
|
|
29090
29481
|
while ((match = apiCallRegex.exec(source)) !== null) {
|
|
@@ -29102,10 +29493,10 @@ function findTablesFromRouters(routerNames, dataDb) {
|
|
|
29102
29493
|
`SELECT DISTINCT router_file FROM ${t("trpc_procedures")} WHERE router_name = ? LIMIT 10000`
|
|
29103
29494
|
).all(routerName);
|
|
29104
29495
|
for (const proc of procs) {
|
|
29105
|
-
const absPath = ensureWithinRoot(
|
|
29106
|
-
if (!
|
|
29496
|
+
const absPath = ensureWithinRoot(resolve35(getProjectRoot(), proc.router_file), getProjectRoot());
|
|
29497
|
+
if (!existsSync58(absPath)) continue;
|
|
29107
29498
|
try {
|
|
29108
|
-
const source =
|
|
29499
|
+
const source = readFileSync44(absPath, "utf-8");
|
|
29109
29500
|
const dbPattern = getConfig().dbAccessPattern ?? "ctx.db.{table}";
|
|
29110
29501
|
const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
|
|
29111
29502
|
const tableRegex = new RegExp(regexStr + "\\.", "g");
|
|
@@ -29380,14 +29771,14 @@ var init_domains = __esm({
|
|
|
29380
29771
|
});
|
|
29381
29772
|
|
|
29382
29773
|
// src/schema-mapper.ts
|
|
29383
|
-
import { readFileSync as
|
|
29774
|
+
import { readFileSync as readFileSync45, existsSync as existsSync59, readdirSync as readdirSync22 } from "fs";
|
|
29384
29775
|
import { join as join32 } from "path";
|
|
29385
29776
|
function parsePrismaSchema() {
|
|
29386
29777
|
const schemaPath = getResolvedPaths().prismaSchemaPath;
|
|
29387
|
-
if (!
|
|
29778
|
+
if (!existsSync59(schemaPath)) {
|
|
29388
29779
|
throw new Error(`Prisma schema not found at ${schemaPath}`);
|
|
29389
29780
|
}
|
|
29390
|
-
const source =
|
|
29781
|
+
const source = readFileSync45(schemaPath, "utf-8");
|
|
29391
29782
|
const models = [];
|
|
29392
29783
|
const sourceLines = source.split("\n");
|
|
29393
29784
|
let i = 0;
|
|
@@ -29445,12 +29836,12 @@ function toSnakeCase(str) {
|
|
|
29445
29836
|
function findColumnUsageInRouters(tableName) {
|
|
29446
29837
|
const usage2 = /* @__PURE__ */ new Map();
|
|
29447
29838
|
const routersDir = getResolvedPaths().routersDir;
|
|
29448
|
-
if (!
|
|
29839
|
+
if (!existsSync59(routersDir)) return usage2;
|
|
29449
29840
|
scanDirectory(routersDir, tableName, usage2);
|
|
29450
29841
|
return usage2;
|
|
29451
29842
|
}
|
|
29452
29843
|
function scanDirectory(dir, tableName, usage2) {
|
|
29453
|
-
const entries =
|
|
29844
|
+
const entries = readdirSync22(dir, { withFileTypes: true });
|
|
29454
29845
|
for (const entry of entries) {
|
|
29455
29846
|
const fullPath = join32(dir, entry.name);
|
|
29456
29847
|
if (entry.isDirectory()) {
|
|
@@ -29462,7 +29853,7 @@ function scanDirectory(dir, tableName, usage2) {
|
|
|
29462
29853
|
}
|
|
29463
29854
|
function scanFile(absPath, tableName, usage2) {
|
|
29464
29855
|
try {
|
|
29465
|
-
const source =
|
|
29856
|
+
const source = readFileSync45(absPath, "utf-8");
|
|
29466
29857
|
if (!source.includes(tableName)) return;
|
|
29467
29858
|
const relPath = absPath.slice(getProjectRoot().length + 1);
|
|
29468
29859
|
const lines = source.split("\n");
|
|
@@ -29507,15 +29898,15 @@ function detectMismatches(models) {
|
|
|
29507
29898
|
}
|
|
29508
29899
|
function findFilesUsingColumn(dir, column, tableName) {
|
|
29509
29900
|
const result = [];
|
|
29510
|
-
if (!
|
|
29511
|
-
const entries =
|
|
29901
|
+
if (!existsSync59(dir)) return result;
|
|
29902
|
+
const entries = readdirSync22(dir, { withFileTypes: true });
|
|
29512
29903
|
for (const entry of entries) {
|
|
29513
29904
|
const fullPath = join32(dir, entry.name);
|
|
29514
29905
|
if (entry.isDirectory()) {
|
|
29515
29906
|
result.push(...findFilesUsingColumn(fullPath, column, tableName));
|
|
29516
29907
|
} else if (entry.name.endsWith(".ts")) {
|
|
29517
29908
|
try {
|
|
29518
|
-
const source =
|
|
29909
|
+
const source = readFileSync45(fullPath, "utf-8");
|
|
29519
29910
|
if (source.includes(tableName) && source.includes(column)) {
|
|
29520
29911
|
result.push(fullPath.slice(getProjectRoot().length + 1));
|
|
29521
29912
|
}
|
|
@@ -29660,12 +30051,12 @@ var init_import_parser = __esm({
|
|
|
29660
30051
|
});
|
|
29661
30052
|
|
|
29662
30053
|
// src/python/import-resolver.ts
|
|
29663
|
-
import { readFileSync as
|
|
29664
|
-
import { resolve as
|
|
30054
|
+
import { readFileSync as readFileSync46, existsSync as existsSync60, readdirSync as readdirSync23 } from "fs";
|
|
30055
|
+
import { resolve as resolve37, join as join33, relative as relative9, dirname as dirname26 } from "path";
|
|
29665
30056
|
function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
|
|
29666
30057
|
const projectRoot = getProjectRoot();
|
|
29667
30058
|
if (level > 0) {
|
|
29668
|
-
let baseDir = dirname26(
|
|
30059
|
+
let baseDir = dirname26(resolve37(projectRoot, fromFile));
|
|
29669
30060
|
for (let i = 1; i < level; i++) {
|
|
29670
30061
|
baseDir = dirname26(baseDir);
|
|
29671
30062
|
}
|
|
@@ -29677,17 +30068,17 @@ function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
|
|
|
29677
30068
|
return tryResolvePythonPath(baseDir, projectRoot);
|
|
29678
30069
|
}
|
|
29679
30070
|
const parts = module.split(".");
|
|
29680
|
-
const candidate = join33(
|
|
30071
|
+
const candidate = join33(resolve37(projectRoot, pythonRoot), ...parts);
|
|
29681
30072
|
return tryResolvePythonPath(candidate, projectRoot);
|
|
29682
30073
|
}
|
|
29683
30074
|
function tryResolvePythonPath(basePath, projectRoot) {
|
|
29684
|
-
if (
|
|
30075
|
+
if (existsSync60(basePath + ".py")) {
|
|
29685
30076
|
return relative9(projectRoot, basePath + ".py");
|
|
29686
30077
|
}
|
|
29687
|
-
if (
|
|
30078
|
+
if (existsSync60(join33(basePath, "__init__.py"))) {
|
|
29688
30079
|
return relative9(projectRoot, join33(basePath, "__init__.py"));
|
|
29689
30080
|
}
|
|
29690
|
-
if (basePath.endsWith(".py") &&
|
|
30081
|
+
if (basePath.endsWith(".py") && existsSync60(basePath)) {
|
|
29691
30082
|
return relative9(projectRoot, basePath);
|
|
29692
30083
|
}
|
|
29693
30084
|
return null;
|
|
@@ -29695,7 +30086,7 @@ function tryResolvePythonPath(basePath, projectRoot) {
|
|
|
29695
30086
|
function walkPythonFiles(dir, excludeDirs) {
|
|
29696
30087
|
const files = [];
|
|
29697
30088
|
try {
|
|
29698
|
-
const entries =
|
|
30089
|
+
const entries = readdirSync23(dir, { withFileTypes: true });
|
|
29699
30090
|
for (const entry of entries) {
|
|
29700
30091
|
if (entry.isDirectory()) {
|
|
29701
30092
|
if (excludeDirs.includes(entry.name)) continue;
|
|
@@ -29710,7 +30101,7 @@ function walkPythonFiles(dir, excludeDirs) {
|
|
|
29710
30101
|
}
|
|
29711
30102
|
function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
|
|
29712
30103
|
const projectRoot = getProjectRoot();
|
|
29713
|
-
const absRoot =
|
|
30104
|
+
const absRoot = resolve37(projectRoot, pythonRoot);
|
|
29714
30105
|
dataDb.exec(`DELETE FROM ${t("py_imports")}`);
|
|
29715
30106
|
const insertStmt = dataDb.prepare(
|
|
29716
30107
|
`INSERT INTO ${t("py_imports")} (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)`
|
|
@@ -29727,7 +30118,7 @@ function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__"
|
|
|
29727
30118
|
const relFile = relative9(projectRoot, absFile);
|
|
29728
30119
|
let source;
|
|
29729
30120
|
try {
|
|
29730
|
-
source =
|
|
30121
|
+
source = readFileSync46(absFile, "utf-8");
|
|
29731
30122
|
} catch {
|
|
29732
30123
|
continue;
|
|
29733
30124
|
}
|
|
@@ -29994,12 +30385,12 @@ var init_route_parser = __esm({
|
|
|
29994
30385
|
});
|
|
29995
30386
|
|
|
29996
30387
|
// src/python/route-indexer.ts
|
|
29997
|
-
import { readFileSync as
|
|
30388
|
+
import { readFileSync as readFileSync47, readdirSync as readdirSync24 } from "fs";
|
|
29998
30389
|
import { join as join34, relative as relative10 } from "path";
|
|
29999
30390
|
function walkPyFiles(dir, excludeDirs) {
|
|
30000
30391
|
const files = [];
|
|
30001
30392
|
try {
|
|
30002
|
-
const entries =
|
|
30393
|
+
const entries = readdirSync24(dir, { withFileTypes: true });
|
|
30003
30394
|
for (const entry of entries) {
|
|
30004
30395
|
if (entry.isDirectory()) {
|
|
30005
30396
|
if (excludeDirs.includes(entry.name)) continue;
|
|
@@ -30027,7 +30418,7 @@ function buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__",
|
|
|
30027
30418
|
const relFile = relative10(projectRoot, absFile);
|
|
30028
30419
|
let source;
|
|
30029
30420
|
try {
|
|
30030
|
-
source =
|
|
30421
|
+
source = readFileSync47(absFile, "utf-8");
|
|
30031
30422
|
} catch {
|
|
30032
30423
|
continue;
|
|
30033
30424
|
}
|
|
@@ -30239,12 +30630,12 @@ var init_model_parser = __esm({
|
|
|
30239
30630
|
});
|
|
30240
30631
|
|
|
30241
30632
|
// src/python/model-indexer.ts
|
|
30242
|
-
import { readFileSync as
|
|
30633
|
+
import { readFileSync as readFileSync48, readdirSync as readdirSync25 } from "fs";
|
|
30243
30634
|
import { join as join35, relative as relative11 } from "path";
|
|
30244
30635
|
function walkPyFiles2(dir, excludeDirs) {
|
|
30245
30636
|
const files = [];
|
|
30246
30637
|
try {
|
|
30247
|
-
const entries =
|
|
30638
|
+
const entries = readdirSync25(dir, { withFileTypes: true });
|
|
30248
30639
|
for (const entry of entries) {
|
|
30249
30640
|
if (entry.isDirectory()) {
|
|
30250
30641
|
if (excludeDirs.includes(entry.name)) continue;
|
|
@@ -30275,7 +30666,7 @@ function buildPythonModelIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__",
|
|
|
30275
30666
|
const relFile = relative11(projectRoot, absFile);
|
|
30276
30667
|
let source;
|
|
30277
30668
|
try {
|
|
30278
|
-
source =
|
|
30669
|
+
source = readFileSync48(absFile, "utf-8");
|
|
30279
30670
|
} catch {
|
|
30280
30671
|
continue;
|
|
30281
30672
|
}
|
|
@@ -30536,7 +30927,7 @@ var init_migration_parser = __esm({
|
|
|
30536
30927
|
});
|
|
30537
30928
|
|
|
30538
30929
|
// src/python/migration-indexer.ts
|
|
30539
|
-
import { readFileSync as
|
|
30930
|
+
import { readFileSync as readFileSync49, readdirSync as readdirSync26 } from "fs";
|
|
30540
30931
|
import { join as join36, relative as relative12 } from "path";
|
|
30541
30932
|
function buildPythonMigrationIndex(dataDb, alembicDir) {
|
|
30542
30933
|
const projectRoot = getProjectRoot();
|
|
@@ -30545,10 +30936,10 @@ function buildPythonMigrationIndex(dataDb, alembicDir) {
|
|
|
30545
30936
|
const versionsDir = join36(absDir, "versions");
|
|
30546
30937
|
let files = [];
|
|
30547
30938
|
try {
|
|
30548
|
-
files =
|
|
30939
|
+
files = readdirSync26(versionsDir).filter((f2) => f2.endsWith(".py")).map((f2) => join36(versionsDir, f2));
|
|
30549
30940
|
} catch {
|
|
30550
30941
|
try {
|
|
30551
|
-
files =
|
|
30942
|
+
files = readdirSync26(absDir).filter((f2) => f2.endsWith(".py") && f2 !== "env.py").map((f2) => join36(absDir, f2));
|
|
30552
30943
|
} catch {
|
|
30553
30944
|
}
|
|
30554
30945
|
}
|
|
@@ -30562,7 +30953,7 @@ function buildPythonMigrationIndex(dataDb, alembicDir) {
|
|
|
30562
30953
|
for (const absFile of files) {
|
|
30563
30954
|
let source;
|
|
30564
30955
|
try {
|
|
30565
|
-
source =
|
|
30956
|
+
source = readFileSync49(absFile, "utf-8");
|
|
30566
30957
|
} catch {
|
|
30567
30958
|
continue;
|
|
30568
30959
|
}
|
|
@@ -30597,7 +30988,7 @@ var init_migration_indexer = __esm({
|
|
|
30597
30988
|
});
|
|
30598
30989
|
|
|
30599
30990
|
// src/python/coupling-detector.ts
|
|
30600
|
-
import { readFileSync as
|
|
30991
|
+
import { readFileSync as readFileSync50, readdirSync as readdirSync27 } from "fs";
|
|
30601
30992
|
import { join as join37, relative as relative13 } from "path";
|
|
30602
30993
|
function buildPythonCouplingIndex(dataDb) {
|
|
30603
30994
|
const projectRoot = getProjectRoot();
|
|
@@ -30634,7 +31025,7 @@ function buildPythonCouplingIndex(dataDb) {
|
|
|
30634
31025
|
const relFile = relative13(projectRoot, absFile);
|
|
30635
31026
|
let source;
|
|
30636
31027
|
try {
|
|
30637
|
-
source =
|
|
31028
|
+
source = readFileSync50(absFile, "utf-8");
|
|
30638
31029
|
} catch {
|
|
30639
31030
|
continue;
|
|
30640
31031
|
}
|
|
@@ -30662,7 +31053,7 @@ function walkFrontendFiles(dir) {
|
|
|
30662
31053
|
const files = [];
|
|
30663
31054
|
const exclude = ["node_modules", ".next", "dist", ".git", "__pycache__", ".venv", "venv"];
|
|
30664
31055
|
try {
|
|
30665
|
-
const entries =
|
|
31056
|
+
const entries = readdirSync27(dir, { withFileTypes: true });
|
|
30666
31057
|
for (const entry of entries) {
|
|
30667
31058
|
if (entry.isDirectory()) {
|
|
30668
31059
|
if (exclude.includes(entry.name)) continue;
|
|
@@ -31040,8 +31431,8 @@ var init_memory_tools = __esm({
|
|
|
31040
31431
|
});
|
|
31041
31432
|
|
|
31042
31433
|
// src/docs-tools.ts
|
|
31043
|
-
import { readFileSync as
|
|
31044
|
-
import { resolve as
|
|
31434
|
+
import { readFileSync as readFileSync51, existsSync as existsSync61 } from "fs";
|
|
31435
|
+
import { resolve as resolve38, basename as basename9 } from "path";
|
|
31045
31436
|
function p4(baseName) {
|
|
31046
31437
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
31047
31438
|
}
|
|
@@ -31096,10 +31487,10 @@ function handleDocsToolCall(name, args2) {
|
|
|
31096
31487
|
}
|
|
31097
31488
|
function loadDocsMap() {
|
|
31098
31489
|
const mapPath = getResolvedPaths().docsMapPath;
|
|
31099
|
-
if (!
|
|
31490
|
+
if (!existsSync61(mapPath)) {
|
|
31100
31491
|
throw new Error(`docs-map.json not found at ${mapPath}`);
|
|
31101
31492
|
}
|
|
31102
|
-
return JSON.parse(
|
|
31493
|
+
return JSON.parse(readFileSync51(mapPath, "utf-8"));
|
|
31103
31494
|
}
|
|
31104
31495
|
function matchesPattern(filePath, pattern) {
|
|
31105
31496
|
const regexStr = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
@@ -31169,13 +31560,13 @@ function extractFrontmatter(content) {
|
|
|
31169
31560
|
}
|
|
31170
31561
|
function extractProcedureNames(routerPath) {
|
|
31171
31562
|
const root = getProjectRoot();
|
|
31172
|
-
const absPath = ensureWithinRoot(
|
|
31173
|
-
if (!
|
|
31174
|
-
const altPath = ensureWithinRoot(
|
|
31175
|
-
if (!
|
|
31176
|
-
return extractProcedureNamesFromContent(
|
|
31563
|
+
const absPath = ensureWithinRoot(resolve38(getResolvedPaths().srcDir, "..", routerPath), root);
|
|
31564
|
+
if (!existsSync61(absPath)) {
|
|
31565
|
+
const altPath = ensureWithinRoot(resolve38(getResolvedPaths().srcDir, "../server/api/routers", basename9(routerPath)), root);
|
|
31566
|
+
if (!existsSync61(altPath)) return [];
|
|
31567
|
+
return extractProcedureNamesFromContent(readFileSync51(altPath, "utf-8"));
|
|
31177
31568
|
}
|
|
31178
|
-
return extractProcedureNamesFromContent(
|
|
31569
|
+
return extractProcedureNamesFromContent(readFileSync51(absPath, "utf-8"));
|
|
31179
31570
|
}
|
|
31180
31571
|
function extractProcedureNamesFromContent(content) {
|
|
31181
31572
|
const procRegex = /\.(?:query|mutation)\s*\(/g;
|
|
@@ -31215,8 +31606,8 @@ function handleDocsAudit(args2) {
|
|
|
31215
31606
|
for (const [mappingId, triggeringFiles] of affectedMappings) {
|
|
31216
31607
|
const mapping = docsMap.mappings.find((m3) => m3.id === mappingId);
|
|
31217
31608
|
if (!mapping) continue;
|
|
31218
|
-
const helpPagePath = ensureWithinRoot(
|
|
31219
|
-
if (!
|
|
31609
|
+
const helpPagePath = ensureWithinRoot(resolve38(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
31610
|
+
if (!existsSync61(helpPagePath)) {
|
|
31220
31611
|
results.push({
|
|
31221
31612
|
helpPage: mapping.helpPage,
|
|
31222
31613
|
mappingId,
|
|
@@ -31228,7 +31619,7 @@ function handleDocsAudit(args2) {
|
|
|
31228
31619
|
});
|
|
31229
31620
|
continue;
|
|
31230
31621
|
}
|
|
31231
|
-
const content =
|
|
31622
|
+
const content = readFileSync51(helpPagePath, "utf-8");
|
|
31232
31623
|
const sections = extractSections(content);
|
|
31233
31624
|
const frontmatter = extractFrontmatter(content);
|
|
31234
31625
|
const staleReasons = [];
|
|
@@ -31268,9 +31659,9 @@ function handleDocsAudit(args2) {
|
|
|
31268
31659
|
});
|
|
31269
31660
|
for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
|
|
31270
31661
|
if (parentId === mappingId) {
|
|
31271
|
-
const guidePath = ensureWithinRoot(
|
|
31272
|
-
if (
|
|
31273
|
-
const guideContent =
|
|
31662
|
+
const guidePath = ensureWithinRoot(resolve38(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
|
|
31663
|
+
if (existsSync61(guidePath)) {
|
|
31664
|
+
const guideContent = readFileSync51(guidePath, "utf-8");
|
|
31274
31665
|
const guideFrontmatter = extractFrontmatter(guideContent);
|
|
31275
31666
|
if (!guideFrontmatter?.lastVerified || status === "STALE") {
|
|
31276
31667
|
results.push({
|
|
@@ -31303,14 +31694,14 @@ function handleDocsCoverage(args2) {
|
|
|
31303
31694
|
const gaps = [];
|
|
31304
31695
|
const mappings = filterDomain ? docsMap.mappings.filter((m3) => m3.id === filterDomain) : docsMap.mappings;
|
|
31305
31696
|
for (const mapping of mappings) {
|
|
31306
|
-
const helpPagePath = ensureWithinRoot(
|
|
31307
|
-
const exists =
|
|
31697
|
+
const helpPagePath = ensureWithinRoot(resolve38(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
31698
|
+
const exists = existsSync61(helpPagePath);
|
|
31308
31699
|
let hasContent = false;
|
|
31309
31700
|
let lineCount = 0;
|
|
31310
31701
|
let lastVerified = null;
|
|
31311
31702
|
let status = null;
|
|
31312
31703
|
if (exists) {
|
|
31313
|
-
const content =
|
|
31704
|
+
const content = readFileSync51(helpPagePath, "utf-8");
|
|
31314
31705
|
lineCount = content.split("\n").length;
|
|
31315
31706
|
hasContent = lineCount > 10;
|
|
31316
31707
|
const frontmatter = extractFrontmatter(content);
|
|
@@ -31651,8 +32042,8 @@ var init_observability_tools = __esm({
|
|
|
31651
32042
|
});
|
|
31652
32043
|
|
|
31653
32044
|
// src/sentinel-db.ts
|
|
31654
|
-
import { existsSync as
|
|
31655
|
-
import { resolve as
|
|
32045
|
+
import { existsSync as existsSync62 } from "fs";
|
|
32046
|
+
import { resolve as resolve39 } from "path";
|
|
31656
32047
|
function parsePortalScope(raw) {
|
|
31657
32048
|
if (!raw) return [];
|
|
31658
32049
|
try {
|
|
@@ -31888,23 +32279,23 @@ function validateFeatures(db, domainFilter) {
|
|
|
31888
32279
|
const missingProcedures = [];
|
|
31889
32280
|
const missingPages = [];
|
|
31890
32281
|
for (const comp of components) {
|
|
31891
|
-
const absPath =
|
|
31892
|
-
if (!
|
|
32282
|
+
const absPath = resolve39(PROJECT_ROOT, comp.component_file);
|
|
32283
|
+
if (!existsSync62(absPath)) {
|
|
31893
32284
|
missingComponents.push(comp.component_file);
|
|
31894
32285
|
}
|
|
31895
32286
|
}
|
|
31896
32287
|
for (const proc of procedures) {
|
|
31897
|
-
const routerPath =
|
|
31898
|
-
if (!
|
|
32288
|
+
const routerPath = resolve39(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
|
|
32289
|
+
if (!existsSync62(routerPath)) {
|
|
31899
32290
|
missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
|
|
31900
32291
|
}
|
|
31901
32292
|
}
|
|
31902
32293
|
for (const page of pages) {
|
|
31903
32294
|
const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
|
|
31904
|
-
const absPath =
|
|
31905
|
-
if (page.page_route.startsWith("/") && !
|
|
31906
|
-
const altPath =
|
|
31907
|
-
if (!
|
|
32295
|
+
const absPath = resolve39(PROJECT_ROOT, routeToPath);
|
|
32296
|
+
if (page.page_route.startsWith("/") && !existsSync62(absPath)) {
|
|
32297
|
+
const altPath = resolve39(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
|
|
32298
|
+
if (!existsSync62(altPath)) {
|
|
31908
32299
|
missingPages.push(page.page_route);
|
|
31909
32300
|
}
|
|
31910
32301
|
}
|
|
@@ -32430,8 +32821,8 @@ var init_sentinel_tools = __esm({
|
|
|
32430
32821
|
});
|
|
32431
32822
|
|
|
32432
32823
|
// src/sentinel-scanner.ts
|
|
32433
|
-
import { readFileSync as
|
|
32434
|
-
import { resolve as
|
|
32824
|
+
import { readFileSync as readFileSync52, existsSync as existsSync63, readdirSync as readdirSync28, statSync as statSync21 } from "fs";
|
|
32825
|
+
import { resolve as resolve40, join as join38, basename as basename10, dirname as dirname27, relative as relative14 } from "path";
|
|
32435
32826
|
function inferDomain(filePath) {
|
|
32436
32827
|
const domains = getConfig().domains;
|
|
32437
32828
|
const path = filePath.toLowerCase();
|
|
@@ -32562,10 +32953,10 @@ function scanComponentExports(dataDb) {
|
|
|
32562
32953
|
const projectRoot = getProjectRoot();
|
|
32563
32954
|
const componentsBase = config.paths.components ?? config.paths.source + "/components";
|
|
32564
32955
|
const componentDirs = [];
|
|
32565
|
-
const basePath =
|
|
32566
|
-
if (
|
|
32956
|
+
const basePath = resolve40(projectRoot, componentsBase);
|
|
32957
|
+
if (existsSync63(basePath)) {
|
|
32567
32958
|
try {
|
|
32568
|
-
const entries =
|
|
32959
|
+
const entries = readdirSync28(basePath, { withFileTypes: true });
|
|
32569
32960
|
for (const entry of entries) {
|
|
32570
32961
|
if (entry.isDirectory()) {
|
|
32571
32962
|
componentDirs.push(componentsBase + "/" + entry.name);
|
|
@@ -32575,12 +32966,12 @@ function scanComponentExports(dataDb) {
|
|
|
32575
32966
|
}
|
|
32576
32967
|
}
|
|
32577
32968
|
for (const dir of componentDirs) {
|
|
32578
|
-
const absDir =
|
|
32579
|
-
if (!
|
|
32969
|
+
const absDir = resolve40(projectRoot, dir);
|
|
32970
|
+
if (!existsSync63(absDir)) continue;
|
|
32580
32971
|
const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
|
|
32581
32972
|
for (const file of files) {
|
|
32582
32973
|
const relPath = relative14(projectRoot, file);
|
|
32583
|
-
const source =
|
|
32974
|
+
const source = readFileSync52(file, "utf-8");
|
|
32584
32975
|
const annotations = parseFeatureAnnotations(source);
|
|
32585
32976
|
if (annotations.length > 0) {
|
|
32586
32977
|
for (const ann of annotations) {
|
|
@@ -32629,11 +33020,11 @@ function scanComponentExports(dataDb) {
|
|
|
32629
33020
|
function walkDir2(dir) {
|
|
32630
33021
|
const results = [];
|
|
32631
33022
|
try {
|
|
32632
|
-
const entries =
|
|
33023
|
+
const entries = readdirSync28(dir);
|
|
32633
33024
|
for (const entry of entries) {
|
|
32634
33025
|
const fullPath = join38(dir, entry);
|
|
32635
33026
|
try {
|
|
32636
|
-
const stat =
|
|
33027
|
+
const stat = statSync21(fullPath);
|
|
32637
33028
|
if (stat.isDirectory()) {
|
|
32638
33029
|
results.push(...walkDir2(fullPath));
|
|
32639
33030
|
} else {
|
|
@@ -33341,7 +33732,7 @@ var init_prompt_analyzer = __esm({
|
|
|
33341
33732
|
});
|
|
33342
33733
|
|
|
33343
33734
|
// src/validation-engine.ts
|
|
33344
|
-
import { existsSync as
|
|
33735
|
+
import { existsSync as existsSync64, readFileSync as readFileSync53 } from "fs";
|
|
33345
33736
|
function p10(baseName) {
|
|
33346
33737
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
33347
33738
|
}
|
|
@@ -33372,7 +33763,7 @@ function validateFile(filePath, projectRoot) {
|
|
|
33372
33763
|
});
|
|
33373
33764
|
return checks;
|
|
33374
33765
|
}
|
|
33375
|
-
if (!
|
|
33766
|
+
if (!existsSync64(absPath)) {
|
|
33376
33767
|
checks.push({
|
|
33377
33768
|
name: "file_exists",
|
|
33378
33769
|
severity: "error",
|
|
@@ -33381,7 +33772,7 @@ function validateFile(filePath, projectRoot) {
|
|
|
33381
33772
|
});
|
|
33382
33773
|
return checks;
|
|
33383
33774
|
}
|
|
33384
|
-
const source =
|
|
33775
|
+
const source = readFileSync53(absPath, "utf-8");
|
|
33385
33776
|
const lines = source.split("\n");
|
|
33386
33777
|
if (activeChecks.rule_compliance !== false) {
|
|
33387
33778
|
for (const ruleSet of config.rules) {
|
|
@@ -33840,7 +34231,7 @@ var init_adr_generator = __esm({
|
|
|
33840
34231
|
});
|
|
33841
34232
|
|
|
33842
34233
|
// src/security-scorer.ts
|
|
33843
|
-
import { existsSync as
|
|
34234
|
+
import { existsSync as existsSync65, readFileSync as readFileSync54 } from "fs";
|
|
33844
34235
|
function p12(baseName) {
|
|
33845
34236
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
33846
34237
|
}
|
|
@@ -33864,12 +34255,12 @@ function scoreFileSecurity(filePath, projectRoot) {
|
|
|
33864
34255
|
}]
|
|
33865
34256
|
};
|
|
33866
34257
|
}
|
|
33867
|
-
if (!
|
|
34258
|
+
if (!existsSync65(absPath)) {
|
|
33868
34259
|
return { riskScore: 0, findings: [] };
|
|
33869
34260
|
}
|
|
33870
34261
|
let source;
|
|
33871
34262
|
try {
|
|
33872
|
-
source =
|
|
34263
|
+
source = readFileSync54(absPath, "utf-8");
|
|
33873
34264
|
} catch {
|
|
33874
34265
|
return { riskScore: 0, findings: [] };
|
|
33875
34266
|
}
|
|
@@ -34159,8 +34550,8 @@ var init_security_scorer = __esm({
|
|
|
34159
34550
|
});
|
|
34160
34551
|
|
|
34161
34552
|
// src/dependency-scorer.ts
|
|
34162
|
-
import { existsSync as
|
|
34163
|
-
import { resolve as
|
|
34553
|
+
import { existsSync as existsSync66, readFileSync as readFileSync55 } from "fs";
|
|
34554
|
+
import { resolve as resolve41 } from "path";
|
|
34164
34555
|
function p13(baseName) {
|
|
34165
34556
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
34166
34557
|
}
|
|
@@ -34192,10 +34583,10 @@ function calculateDepRisk(factors) {
|
|
|
34192
34583
|
return Math.min(100, risk);
|
|
34193
34584
|
}
|
|
34194
34585
|
function getInstalledPackages(projectRoot) {
|
|
34195
|
-
const pkgPath =
|
|
34196
|
-
if (!
|
|
34586
|
+
const pkgPath = resolve41(projectRoot, "package.json");
|
|
34587
|
+
if (!existsSync66(pkgPath)) return /* @__PURE__ */ new Map();
|
|
34197
34588
|
try {
|
|
34198
|
-
const pkg = JSON.parse(
|
|
34589
|
+
const pkg = JSON.parse(readFileSync55(pkgPath, "utf-8"));
|
|
34199
34590
|
const packages = /* @__PURE__ */ new Map();
|
|
34200
34591
|
for (const [name, version] of Object.entries(pkg.dependencies ?? {})) {
|
|
34201
34592
|
packages.set(name, version);
|
|
@@ -34801,8 +35192,8 @@ var init_regression_detector = __esm({
|
|
|
34801
35192
|
|
|
34802
35193
|
// src/knowledge-indexer.ts
|
|
34803
35194
|
import { createHash as createHash16 } from "crypto";
|
|
34804
|
-
import { readFileSync as
|
|
34805
|
-
import { resolve as
|
|
35195
|
+
import { readFileSync as readFileSync56, readdirSync as readdirSync29, statSync as statSync22, existsSync as existsSync67 } from "fs";
|
|
35196
|
+
import { resolve as resolve42, relative as relative15, basename as basename11, extname as extname2 } from "path";
|
|
34806
35197
|
function getKnowledgePaths() {
|
|
34807
35198
|
const resolved = getResolvedPaths();
|
|
34808
35199
|
const config = getConfig();
|
|
@@ -34828,9 +35219,9 @@ function discoverMarkdownFiles(baseDir) {
|
|
|
34828
35219
|
const files = [];
|
|
34829
35220
|
function walk(dir) {
|
|
34830
35221
|
try {
|
|
34831
|
-
const entries =
|
|
35222
|
+
const entries = readdirSync29(dir, { withFileTypes: true });
|
|
34832
35223
|
for (const entry of entries) {
|
|
34833
|
-
const fullPath =
|
|
35224
|
+
const fullPath = resolve42(dir, entry.name);
|
|
34834
35225
|
if (entry.isDirectory()) {
|
|
34835
35226
|
if (entry.name === "archive" && dir.includes("session-state")) continue;
|
|
34836
35227
|
if (entry.name === "archive" && dir.includes("status")) continue;
|
|
@@ -34892,14 +35283,17 @@ function hashContent2(content) {
|
|
|
34892
35283
|
}
|
|
34893
35284
|
function parseCRTable(content) {
|
|
34894
35285
|
const rules = [];
|
|
34895
|
-
const
|
|
34896
|
-
|
|
34897
|
-
|
|
35286
|
+
for (const line of content.split("\n")) {
|
|
35287
|
+
const trimmed = line.trim();
|
|
35288
|
+
if (!trimmed.startsWith("|")) continue;
|
|
35289
|
+
const cells = trimmed.split("|").slice(1, -1).map((c2) => c2.trim());
|
|
35290
|
+
if (cells.length < 3) continue;
|
|
35291
|
+
if (!/^CR-\d+$/.test(cells[0])) continue;
|
|
34898
35292
|
rules.push({
|
|
34899
|
-
rule_id:
|
|
34900
|
-
rule_text:
|
|
34901
|
-
vr_type:
|
|
34902
|
-
reference_path:
|
|
35293
|
+
rule_id: cells[0],
|
|
35294
|
+
rule_text: cells[1],
|
|
35295
|
+
vr_type: cells[2],
|
|
35296
|
+
reference_path: (cells[3] ?? "").replace(/^\[|\]$/g, "").replace(/\(.*\)/, "").trim()
|
|
34903
35297
|
});
|
|
34904
35298
|
}
|
|
34905
35299
|
return rules;
|
|
@@ -35082,7 +35476,7 @@ function buildCrossReferences(db) {
|
|
|
35082
35476
|
return edgeCount;
|
|
35083
35477
|
}
|
|
35084
35478
|
function indexAllKnowledge(db) {
|
|
35085
|
-
const stats = { filesIndexed: 0, chunksCreated: 0, edgesCreated: 0 };
|
|
35479
|
+
const stats = { filesIndexed: 0, chunksCreated: 0, edgesCreated: 0, indexedPaths: [] };
|
|
35086
35480
|
const paths = getKnowledgePaths();
|
|
35087
35481
|
const insertDoc = db.prepare(
|
|
35088
35482
|
"INSERT INTO knowledge_documents (file_path, category, title, description, content_hash, indexed_at, indexed_at_epoch) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
|
@@ -35109,11 +35503,11 @@ function indexAllKnowledge(db) {
|
|
|
35109
35503
|
files.push(...memFiles);
|
|
35110
35504
|
} catch {
|
|
35111
35505
|
}
|
|
35112
|
-
if (
|
|
35506
|
+
if (existsSync67(paths.plansDir)) {
|
|
35113
35507
|
const planFiles = discoverMarkdownFiles(paths.plansDir);
|
|
35114
35508
|
files.push(...planFiles);
|
|
35115
35509
|
}
|
|
35116
|
-
if (
|
|
35510
|
+
if (existsSync67(paths.docsDir)) {
|
|
35117
35511
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
35118
35512
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
35119
35513
|
files.push(...docsFiles);
|
|
@@ -35156,8 +35550,8 @@ function indexAllKnowledge(db) {
|
|
|
35156
35550
|
} catch {
|
|
35157
35551
|
}
|
|
35158
35552
|
for (const filePath of files) {
|
|
35159
|
-
if (!
|
|
35160
|
-
const content =
|
|
35553
|
+
if (!existsSync67(filePath)) continue;
|
|
35554
|
+
const content = readFileSync56(filePath, "utf-8");
|
|
35161
35555
|
const hash = hashContent2(content);
|
|
35162
35556
|
const relPath = filePath.startsWith(paths.claudeDir) ? relative15(paths.claudeDir, filePath) : filePath.startsWith(paths.plansDir) ? "plans/" + relative15(paths.plansDir, filePath) : filePath.startsWith(paths.docsDir) ? "docs/" + relative15(paths.docsDir, filePath) : filePath.startsWith(paths.memoryDir) ? `memory/${relative15(paths.memoryDir, filePath)}` : basename11(filePath);
|
|
35163
35557
|
const category = categorizeFile(filePath);
|
|
@@ -35166,6 +35560,7 @@ function indexAllKnowledge(db) {
|
|
|
35166
35560
|
const result = insertDoc.run(relPath, category, title, description, hash, nowIso2, nowEpoch);
|
|
35167
35561
|
const docId = result.lastInsertRowid;
|
|
35168
35562
|
stats.filesIndexed++;
|
|
35563
|
+
stats.indexedPaths.push(filePath);
|
|
35169
35564
|
const sections = parseSections(content, filePath);
|
|
35170
35565
|
for (const section of sections) {
|
|
35171
35566
|
if (section.content.length > 10) {
|
|
@@ -35234,7 +35629,7 @@ function indexAllKnowledge(db) {
|
|
|
35234
35629
|
stats.chunksCreated++;
|
|
35235
35630
|
}
|
|
35236
35631
|
}
|
|
35237
|
-
if (fileNameLower ===
|
|
35632
|
+
if (fileNameLower === CORRECTIONS_FILENAME) {
|
|
35238
35633
|
const corrections = parseCorrections(content);
|
|
35239
35634
|
for (const c2 of corrections) {
|
|
35240
35635
|
insertChunk.run(
|
|
@@ -35292,17 +35687,17 @@ function isKnowledgeStale(db) {
|
|
|
35292
35687
|
files.push(...discoverMarkdownFiles(paths.memoryDir));
|
|
35293
35688
|
} catch {
|
|
35294
35689
|
}
|
|
35295
|
-
if (
|
|
35690
|
+
if (existsSync67(paths.plansDir)) {
|
|
35296
35691
|
files.push(...discoverMarkdownFiles(paths.plansDir));
|
|
35297
35692
|
}
|
|
35298
|
-
if (
|
|
35693
|
+
if (existsSync67(paths.docsDir)) {
|
|
35299
35694
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
35300
35695
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
35301
35696
|
files.push(...docsFiles);
|
|
35302
35697
|
}
|
|
35303
35698
|
for (const filePath of files) {
|
|
35304
35699
|
try {
|
|
35305
|
-
const stat =
|
|
35700
|
+
const stat = statSync22(filePath);
|
|
35306
35701
|
if (stat.mtimeMs > lastIndexTime) return true;
|
|
35307
35702
|
} catch {
|
|
35308
35703
|
continue;
|
|
@@ -35314,18 +35709,19 @@ function indexIfStale(db) {
|
|
|
35314
35709
|
if (isKnowledgeStale(db)) {
|
|
35315
35710
|
return indexAllKnowledge(db);
|
|
35316
35711
|
}
|
|
35317
|
-
return { filesIndexed: 0, chunksCreated: 0, edgesCreated: 0 };
|
|
35712
|
+
return { filesIndexed: 0, chunksCreated: 0, edgesCreated: 0, indexedPaths: [] };
|
|
35318
35713
|
}
|
|
35319
35714
|
var init_knowledge_indexer = __esm({
|
|
35320
35715
|
"src/knowledge-indexer.ts"() {
|
|
35321
35716
|
"use strict";
|
|
35717
|
+
init_corrections_md();
|
|
35322
35718
|
init_config();
|
|
35323
35719
|
}
|
|
35324
35720
|
});
|
|
35325
35721
|
|
|
35326
35722
|
// src/knowledge-tools.ts
|
|
35327
|
-
import { readFileSync as
|
|
35328
|
-
import {
|
|
35723
|
+
import { readFileSync as readFileSync57, writeFileSync as writeFileSync17, readdirSync as readdirSync30 } from "fs";
|
|
35724
|
+
import { basename as basename12 } from "path";
|
|
35329
35725
|
function p16(baseName) {
|
|
35330
35726
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
35331
35727
|
}
|
|
@@ -36080,30 +36476,23 @@ function handleCorrect(db, args2) {
|
|
|
36080
36476
|
if (!wrong || !correction || !rule) {
|
|
36081
36477
|
return text15("Error: wrong, correction, and rule are all required.");
|
|
36082
36478
|
}
|
|
36083
|
-
const
|
|
36479
|
+
const correctionsFile = correctionsPath(getResolvedPaths().memoryDir);
|
|
36084
36480
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
36085
36481
|
const title = rule.slice(0, 60);
|
|
36086
|
-
const
|
|
36482
|
+
const correctionEntry = {
|
|
36087
36483
|
date: today,
|
|
36088
36484
|
title,
|
|
36089
36485
|
wrong,
|
|
36090
36486
|
correction,
|
|
36091
36487
|
rule,
|
|
36092
36488
|
...crRule ? { extra: { CR: crRule } } : {}
|
|
36093
|
-
}
|
|
36489
|
+
};
|
|
36094
36490
|
let existing = "";
|
|
36095
36491
|
try {
|
|
36096
|
-
existing =
|
|
36492
|
+
existing = readFileSync57(correctionsFile, "utf-8");
|
|
36097
36493
|
} catch {
|
|
36098
36494
|
}
|
|
36099
|
-
|
|
36100
|
-
if (archiveIdx > 0) {
|
|
36101
|
-
const before = existing.slice(0, archiveIdx);
|
|
36102
|
-
const after = existing.slice(archiveIdx);
|
|
36103
|
-
writeFileSync17(correctionsPath, before + entry + after);
|
|
36104
|
-
} else {
|
|
36105
|
-
appendFileSync5(correctionsPath, entry);
|
|
36106
|
-
}
|
|
36495
|
+
writeFileSync17(correctionsFile, insertCorrectionEntry(existing, correctionEntry));
|
|
36107
36496
|
const doc = db.prepare("SELECT id FROM knowledge_documents WHERE file_path LIKE ?").get("%corrections.md");
|
|
36108
36497
|
if (doc) {
|
|
36109
36498
|
db.prepare("INSERT INTO knowledge_chunks (document_id, chunk_type, heading, content, metadata) VALUES (?, ?, ?, ?, ?)").run(
|
|
@@ -36275,7 +36664,7 @@ function handleGaps(db, args2) {
|
|
|
36275
36664
|
} else if (checkType === "routers") {
|
|
36276
36665
|
try {
|
|
36277
36666
|
const routersDir = getResolvedPaths().routersDir;
|
|
36278
|
-
const routerFiles =
|
|
36667
|
+
const routerFiles = readdirSync30(routersDir).filter((f2) => f2.endsWith(".ts") && !f2.startsWith("_"));
|
|
36279
36668
|
lines.push(`| Router | Knowledge Hits | Status |`);
|
|
36280
36669
|
lines.push(`|--------|----------------|--------|`);
|
|
36281
36670
|
for (const file of routerFiles) {
|
|
@@ -37050,8 +37439,8 @@ var init_framework_supports = __esm({
|
|
|
37050
37439
|
});
|
|
37051
37440
|
|
|
37052
37441
|
// src/tools.ts
|
|
37053
|
-
import { readFileSync as
|
|
37054
|
-
import { resolve as
|
|
37442
|
+
import { readFileSync as readFileSync58, existsSync as existsSync69 } from "fs";
|
|
37443
|
+
import { resolve as resolve44, basename as basename13 } from "path";
|
|
37055
37444
|
function prefix2() {
|
|
37056
37445
|
return getConfig().toolPrefix;
|
|
37057
37446
|
}
|
|
@@ -37086,7 +37475,7 @@ function ensureIndexes(dataDb, codegraphDb, force = false) {
|
|
|
37086
37475
|
if (config.python?.root) {
|
|
37087
37476
|
const pythonRoot = config.python.root;
|
|
37088
37477
|
const excludeDirs = config.python.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
|
|
37089
|
-
if (force || isPythonDataStale(dataDb,
|
|
37478
|
+
if (force || isPythonDataStale(dataDb, resolve44(getProjectRoot(), pythonRoot))) {
|
|
37090
37479
|
const pyImports = buildPythonImportIndex(dataDb, pythonRoot, excludeDirs);
|
|
37091
37480
|
results.push(`Python imports: ${pyImports}`);
|
|
37092
37481
|
const pyRoutes = buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs);
|
|
@@ -37469,9 +37858,9 @@ function handleContext(file, dataDb, codegraphDb) {
|
|
|
37469
37858
|
try {
|
|
37470
37859
|
const resolvedPaths = getResolvedPaths();
|
|
37471
37860
|
const root = getProjectRoot();
|
|
37472
|
-
const absFilePath = ensureWithinRoot(
|
|
37473
|
-
if (
|
|
37474
|
-
const fileContent =
|
|
37861
|
+
const absFilePath = ensureWithinRoot(resolve44(resolvedPaths.srcDir, "..", file), root);
|
|
37862
|
+
if (existsSync69(absFilePath)) {
|
|
37863
|
+
const fileContent = readFileSync58(absFilePath, "utf-8").slice(0, 3e3);
|
|
37475
37864
|
const keywords = [];
|
|
37476
37865
|
if (fileContent.includes("ctx.db")) keywords.push("database", "schema");
|
|
37477
37866
|
if (fileContent.includes("BigInt") || fileContent.includes("Decimal")) keywords.push("BigInt", "serialization");
|
|
@@ -37910,11 +38299,11 @@ function handleSchema(args2) {
|
|
|
37910
38299
|
lines.push("Checking all column references against Prisma schema...");
|
|
37911
38300
|
lines.push("");
|
|
37912
38301
|
const projectRoot = getProjectRoot();
|
|
37913
|
-
const absPath = ensureWithinRoot(
|
|
37914
|
-
if (!
|
|
38302
|
+
const absPath = ensureWithinRoot(resolve44(projectRoot, file), projectRoot);
|
|
38303
|
+
if (!existsSync69(absPath)) {
|
|
37915
38304
|
return text17(`File not found: ${file}`);
|
|
37916
38305
|
}
|
|
37917
|
-
const source =
|
|
38306
|
+
const source = readFileSync58(absPath, "utf-8");
|
|
37918
38307
|
const config = getConfig();
|
|
37919
38308
|
const dbPattern = config.dbAccessPattern ?? "ctx.db.{table}";
|
|
37920
38309
|
const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
|
|
@@ -38347,8 +38736,8 @@ var init_startup_health = __esm({
|
|
|
38347
38736
|
|
|
38348
38737
|
// src/server.ts
|
|
38349
38738
|
var server_exports = {};
|
|
38350
|
-
import { readFileSync as
|
|
38351
|
-
import { resolve as
|
|
38739
|
+
import { readFileSync as readFileSync59 } from "fs";
|
|
38740
|
+
import { resolve as resolve45, dirname as dirname28 } from "path";
|
|
38352
38741
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
38353
38742
|
function pruneMemoryOnStartup() {
|
|
38354
38743
|
try {
|
|
@@ -38396,7 +38785,7 @@ var init_server = __esm({
|
|
|
38396
38785
|
__dirname5 = dirname28(fileURLToPath6(import.meta.url));
|
|
38397
38786
|
PKG_VERSION = (() => {
|
|
38398
38787
|
try {
|
|
38399
|
-
const pkg = JSON.parse(
|
|
38788
|
+
const pkg = JSON.parse(readFileSync59(resolve45(__dirname5, "..", "package.json"), "utf-8"));
|
|
38400
38789
|
return pkg.version ?? "0.0.0";
|
|
38401
38790
|
} catch {
|
|
38402
38791
|
return "0.0.0";
|
|
@@ -38699,19 +39088,19 @@ var config_upgrade_exports = {};
|
|
|
38699
39088
|
__export(config_upgrade_exports, {
|
|
38700
39089
|
runConfigUpgrade: () => runConfigUpgrade
|
|
38701
39090
|
});
|
|
38702
|
-
import { existsSync as
|
|
38703
|
-
import { resolve as
|
|
39091
|
+
import { existsSync as existsSync70, readFileSync as readFileSync60, writeFileSync as writeFileSync18, copyFileSync as copyFileSync3, unlinkSync as unlinkSync6 } from "fs";
|
|
39092
|
+
import { resolve as resolve46 } from "path";
|
|
38704
39093
|
import { parse as parseYaml6 } from "yaml";
|
|
38705
39094
|
async function runConfigUpgrade(opts = {}) {
|
|
38706
39095
|
const cwd = opts.cwd ?? process.cwd();
|
|
38707
|
-
const configPath =
|
|
39096
|
+
const configPath = resolve46(cwd, "massu.config.yaml");
|
|
38708
39097
|
const bakPath = `${configPath}.bak`;
|
|
38709
39098
|
const log = opts.silent ? () => {
|
|
38710
39099
|
} : (s) => process.stdout.write(s);
|
|
38711
39100
|
const err = opts.silent ? () => {
|
|
38712
39101
|
} : (s) => process.stderr.write(s);
|
|
38713
39102
|
if (opts.rollback) {
|
|
38714
|
-
if (!
|
|
39103
|
+
if (!existsSync70(bakPath)) {
|
|
38715
39104
|
const message = `No backup found at ${bakPath}`;
|
|
38716
39105
|
err(message + "\n");
|
|
38717
39106
|
return { exitCode: 1, action: "none", message };
|
|
@@ -38727,14 +39116,14 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
38727
39116
|
return { exitCode: 2, action: "none", message };
|
|
38728
39117
|
}
|
|
38729
39118
|
}
|
|
38730
|
-
if (!
|
|
39119
|
+
if (!existsSync70(configPath)) {
|
|
38731
39120
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
38732
39121
|
err(message + "\n");
|
|
38733
39122
|
return { exitCode: 1, action: "none", message };
|
|
38734
39123
|
}
|
|
38735
39124
|
let existing;
|
|
38736
39125
|
try {
|
|
38737
|
-
const content =
|
|
39126
|
+
const content = readFileSync60(configPath, "utf-8");
|
|
38738
39127
|
const parsed = parseYaml6(content);
|
|
38739
39128
|
if (!parsed || typeof parsed !== "object") {
|
|
38740
39129
|
throw new Error("config is not a YAML object");
|
|
@@ -38757,7 +39146,7 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
38757
39146
|
fingerprint: computeFingerprint(detection)
|
|
38758
39147
|
};
|
|
38759
39148
|
try {
|
|
38760
|
-
const original =
|
|
39149
|
+
const original = readFileSync60(configPath, "utf-8");
|
|
38761
39150
|
writeFileSync18(bakPath, original, "utf-8");
|
|
38762
39151
|
} catch (e2) {
|
|
38763
39152
|
const message = `Failed to write backup: ${e2 instanceof Error ? e2.message : String(e2)}`;
|
|
@@ -38791,8 +39180,8 @@ var config_check_drift_exports = {};
|
|
|
38791
39180
|
__export(config_check_drift_exports, {
|
|
38792
39181
|
runConfigCheckDrift: () => runConfigCheckDrift
|
|
38793
39182
|
});
|
|
38794
|
-
import { existsSync as
|
|
38795
|
-
import { resolve as
|
|
39183
|
+
import { existsSync as existsSync71, readFileSync as readFileSync61 } from "fs";
|
|
39184
|
+
import { resolve as resolve47 } from "path";
|
|
38796
39185
|
import { parse as parseYaml7 } from "yaml";
|
|
38797
39186
|
function renderChanges(changes) {
|
|
38798
39187
|
if (changes.length === 0) return "(none)\n";
|
|
@@ -38800,12 +39189,12 @@ function renderChanges(changes) {
|
|
|
38800
39189
|
}
|
|
38801
39190
|
async function runConfigCheckDrift(opts = {}) {
|
|
38802
39191
|
const cwd = opts.cwd ?? process.cwd();
|
|
38803
|
-
const configPath =
|
|
39192
|
+
const configPath = resolve47(cwd, "massu.config.yaml");
|
|
38804
39193
|
const log = opts.silent ? () => {
|
|
38805
39194
|
} : (s) => process.stdout.write(s);
|
|
38806
39195
|
const err = opts.silent ? () => {
|
|
38807
39196
|
} : (s) => process.stderr.write(s);
|
|
38808
|
-
if (!
|
|
39197
|
+
if (!existsSync71(configPath)) {
|
|
38809
39198
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
38810
39199
|
err(message + "\n");
|
|
38811
39200
|
return {
|
|
@@ -38819,7 +39208,7 @@ async function runConfigCheckDrift(opts = {}) {
|
|
|
38819
39208
|
}
|
|
38820
39209
|
let config;
|
|
38821
39210
|
try {
|
|
38822
|
-
const content =
|
|
39211
|
+
const content = readFileSync61(configPath, "utf-8");
|
|
38823
39212
|
const parsed = parseYaml7(content);
|
|
38824
39213
|
if (!parsed || typeof parsed !== "object") {
|
|
38825
39214
|
throw new Error("config is not a YAML object");
|
|
@@ -38886,8 +39275,8 @@ var init_config_check_drift = __esm({
|
|
|
38886
39275
|
});
|
|
38887
39276
|
|
|
38888
39277
|
// src/cli.ts
|
|
38889
|
-
import { readFileSync as
|
|
38890
|
-
import { resolve as
|
|
39278
|
+
import { readFileSync as readFileSync62 } from "fs";
|
|
39279
|
+
import { resolve as resolve48, dirname as dirname29 } from "path";
|
|
38891
39280
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
38892
39281
|
|
|
38893
39282
|
// src/lib/node-bootstrap.ts
|
|
@@ -39389,7 +39778,7 @@ Examples:
|
|
|
39389
39778
|
}
|
|
39390
39779
|
function printVersion() {
|
|
39391
39780
|
try {
|
|
39392
|
-
const pkg = JSON.parse(
|
|
39781
|
+
const pkg = JSON.parse(readFileSync62(resolve48(__dirname6, "../package.json"), "utf-8"));
|
|
39393
39782
|
console.log(`massu v${pkg.version}`);
|
|
39394
39783
|
} catch {
|
|
39395
39784
|
console.log("massu v0.1.0");
|