@shrkcrft/cli 0.1.0-alpha.2 → 0.1.0-alpha.20
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/dist/audit/knowledge-audit-llm.d.ts +19 -0
- package/dist/audit/knowledge-audit-llm.d.ts.map +1 -0
- package/dist/audit/knowledge-audit-llm.js +164 -0
- package/dist/audit/knowledge-audit.d.ts +61 -0
- package/dist/audit/knowledge-audit.d.ts.map +1 -0
- package/dist/audit/knowledge-audit.js +203 -0
- package/dist/audit/knowledge-fix-plan-llm.d.ts +11 -0
- package/dist/audit/knowledge-fix-plan-llm.d.ts.map +1 -0
- package/dist/audit/knowledge-fix-plan-llm.js +141 -0
- package/dist/audit/knowledge-fix-plan.d.ts +41 -0
- package/dist/audit/knowledge-fix-plan.d.ts.map +1 -0
- package/dist/audit/knowledge-fix-plan.js +125 -0
- package/dist/audit/pipeline-audit-llm.d.ts +11 -0
- package/dist/audit/pipeline-audit-llm.d.ts.map +1 -0
- package/dist/audit/pipeline-audit-llm.js +134 -0
- package/dist/audit/pipeline-audit.d.ts +69 -0
- package/dist/audit/pipeline-audit.d.ts.map +1 -0
- package/dist/audit/pipeline-audit.js +166 -0
- package/dist/audit/templates-audit-llm.d.ts +19 -0
- package/dist/audit/templates-audit-llm.d.ts.map +1 -0
- package/dist/audit/templates-audit-llm.js +207 -0
- package/dist/audit/templates-audit.d.ts +63 -0
- package/dist/audit/templates-audit.d.ts.map +1 -0
- package/dist/audit/templates-audit.js +171 -0
- package/dist/audit/templates-fix-plan-llm.d.ts +19 -0
- package/dist/audit/templates-fix-plan-llm.d.ts.map +1 -0
- package/dist/audit/templates-fix-plan-llm.js +162 -0
- package/dist/audit/templates-fix-plan.d.ts +37 -0
- package/dist/audit/templates-fix-plan.d.ts.map +1 -0
- package/dist/audit/templates-fix-plan.js +174 -0
- package/dist/command-registry.d.ts +28 -0
- package/dist/command-registry.d.ts.map +1 -1
- package/dist/command-registry.js +91 -1
- package/dist/commands/ai-status.command.d.ts +19 -0
- package/dist/commands/ai-status.command.d.ts.map +1 -0
- package/dist/commands/ai-status.command.js +94 -0
- package/dist/commands/api-diff.command.d.ts +11 -0
- package/dist/commands/api-diff.command.d.ts.map +1 -0
- package/dist/commands/api-diff.command.js +144 -0
- package/dist/commands/apply.command.d.ts.map +1 -1
- package/dist/commands/apply.command.js +10 -2
- package/dist/commands/arch.command.d.ts +9 -0
- package/dist/commands/arch.command.d.ts.map +1 -0
- package/dist/commands/arch.command.js +186 -0
- package/dist/commands/ask.command.d.ts.map +1 -1
- package/dist/commands/ask.command.js +10 -9
- package/dist/commands/cache-align.command.d.ts +12 -0
- package/dist/commands/cache-align.command.d.ts.map +1 -0
- package/dist/commands/cache-align.command.js +78 -0
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +19 -2
- package/dist/commands/code-intel.command.d.ts +18 -0
- package/dist/commands/code-intel.command.d.ts.map +1 -0
- package/dist/commands/code-intel.command.js +146 -0
- package/dist/commands/codemod.command.d.ts.map +1 -1
- package/dist/commands/codemod.command.js +27 -6
- package/dist/commands/command-catalog.d.ts +15 -3
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +387 -34
- package/dist/commands/commands.command.d.ts.map +1 -1
- package/dist/commands/commands.command.js +4 -4
- package/dist/commands/completion.command.d.ts +10 -0
- package/dist/commands/completion.command.d.ts.map +1 -0
- package/dist/commands/completion.command.js +121 -0
- package/dist/commands/compress.command.d.ts +8 -0
- package/dist/commands/compress.command.d.ts.map +1 -0
- package/dist/commands/compress.command.js +147 -0
- package/dist/commands/constructs.command.d.ts.map +1 -1
- package/dist/commands/constructs.command.js +89 -23
- package/dist/commands/context.command.d.ts.map +1 -1
- package/dist/commands/context.command.js +121 -1
- package/dist/commands/contract-gate.command.d.ts.map +1 -1
- package/dist/commands/contract-gate.command.js +5 -1
- package/dist/commands/delegate.command.d.ts +65 -0
- package/dist/commands/delegate.command.d.ts.map +1 -0
- package/dist/commands/delegate.command.js +657 -0
- package/dist/commands/deps-audit.command.d.ts +23 -0
- package/dist/commands/deps-audit.command.d.ts.map +1 -0
- package/dist/commands/deps-audit.command.js +270 -0
- package/dist/commands/dev.command.d.ts.map +1 -1
- package/dist/commands/dev.command.js +5 -1
- package/dist/commands/diff-check.command.d.ts +30 -0
- package/dist/commands/diff-check.command.d.ts.map +1 -0
- package/dist/commands/diff-check.command.js +210 -0
- package/dist/commands/doctor.command.d.ts.map +1 -1
- package/dist/commands/doctor.command.js +162 -10
- package/dist/commands/export.command.d.ts.map +1 -1
- package/dist/commands/export.command.js +76 -3
- package/dist/commands/framework.command.d.ts +12 -0
- package/dist/commands/framework.command.d.ts.map +1 -0
- package/dist/commands/framework.command.js +180 -0
- package/dist/commands/gate.command.d.ts +15 -0
- package/dist/commands/gate.command.d.ts.map +1 -0
- package/dist/commands/gate.command.js +300 -0
- package/dist/commands/gen.command.d.ts.map +1 -1
- package/dist/commands/gen.command.js +13 -1
- package/dist/commands/graph-code-subverbs.d.ts +33 -0
- package/dist/commands/graph-code-subverbs.d.ts.map +1 -0
- package/dist/commands/graph-code-subverbs.js +1366 -0
- package/dist/commands/graph.command.d.ts.map +1 -1
- package/dist/commands/graph.command.js +31 -2
- package/dist/commands/help.command.d.ts +4 -3
- package/dist/commands/help.command.d.ts.map +1 -1
- package/dist/commands/help.command.js +86 -18
- package/dist/commands/helper.command.js +1 -1
- package/dist/commands/impact.command.d.ts.map +1 -1
- package/dist/commands/impact.command.js +171 -1
- package/dist/commands/import.command.d.ts.map +1 -1
- package/dist/commands/import.command.js +121 -5
- package/dist/commands/ingest.command.d.ts.map +1 -1
- package/dist/commands/ingest.command.js +5 -1
- package/dist/commands/init.command.d.ts.map +1 -1
- package/dist/commands/init.command.js +174 -7
- package/dist/commands/knowledge-author.command.d.ts.map +1 -1
- package/dist/commands/knowledge-author.command.js +9 -0
- package/dist/commands/knowledge-propose.command.d.ts.map +1 -1
- package/dist/commands/knowledge-propose.command.js +4 -2
- package/dist/commands/knowledge.command.d.ts.map +1 -1
- package/dist/commands/knowledge.command.js +26 -3
- package/dist/commands/migrate.command.d.ts +13 -0
- package/dist/commands/migrate.command.d.ts.map +1 -0
- package/dist/commands/migrate.command.js +152 -0
- package/dist/commands/move-plan.command.d.ts +23 -0
- package/dist/commands/move-plan.command.d.ts.map +1 -0
- package/dist/commands/move-plan.command.js +360 -0
- package/dist/commands/packs-new.d.ts +1 -1
- package/dist/commands/packs-new.d.ts.map +1 -1
- package/dist/commands/packs-new.js +5 -36
- package/dist/commands/packs.command.d.ts.map +1 -1
- package/dist/commands/packs.command.js +2 -10
- package/dist/commands/plan-context.command.d.ts +11 -0
- package/dist/commands/plan-context.command.d.ts.map +1 -0
- package/dist/commands/plan-context.command.js +85 -0
- package/dist/commands/preflight.command.d.ts.map +1 -1
- package/dist/commands/preflight.command.js +15 -0
- package/dist/commands/profiles.command.js +4 -4
- package/dist/commands/recommend.command.d.ts +6 -0
- package/dist/commands/recommend.command.d.ts.map +1 -1
- package/dist/commands/recommend.command.js +119 -5
- package/dist/commands/release.command.js +13 -13
- package/dist/commands/rule-graph-subverbs.d.ts +3 -0
- package/dist/commands/rule-graph-subverbs.d.ts.map +1 -0
- package/dist/commands/rule-graph-subverbs.js +132 -0
- package/dist/commands/rules.command.d.ts.map +1 -1
- package/dist/commands/rules.command.js +20 -3
- package/dist/commands/scaffold-validate.command.d.ts +22 -0
- package/dist/commands/scaffold-validate.command.d.ts.map +1 -0
- package/dist/commands/scaffold-validate.command.js +215 -0
- package/dist/commands/search-structural.command.d.ts +18 -0
- package/dist/commands/search-structural.command.d.ts.map +1 -0
- package/dist/commands/search-structural.command.js +376 -0
- package/dist/commands/search.command.js +1 -1
- package/dist/commands/smart-context.command.d.ts +67 -0
- package/dist/commands/smart-context.command.d.ts.map +1 -0
- package/dist/commands/smart-context.command.js +4728 -0
- package/dist/commands/spike.command.d.ts +22 -0
- package/dist/commands/spike.command.d.ts.map +1 -0
- package/dist/commands/spike.command.js +235 -0
- package/dist/commands/surface.command.d.ts +1 -0
- package/dist/commands/surface.command.d.ts.map +1 -1
- package/dist/commands/surface.command.js +10 -3
- package/dist/commands/task-context.command.d.ts.map +1 -1
- package/dist/commands/task-context.command.js +5 -17
- package/dist/commands/task.command.d.ts.map +1 -1
- package/dist/commands/task.command.js +8 -2
- package/dist/commands/template-quality.command.d.ts.map +1 -1
- package/dist/commands/template-quality.command.js +39 -3
- package/dist/commands/templates.command.d.ts.map +1 -1
- package/dist/commands/templates.command.js +37 -2
- package/dist/commands/tests.command.d.ts.map +1 -1
- package/dist/commands/tests.command.js +13 -2
- package/dist/commands/watch.command.d.ts +26 -0
- package/dist/commands/watch.command.d.ts.map +1 -0
- package/dist/commands/watch.command.js +456 -0
- package/dist/dashboard/code-intelligence-data.d.ts +33 -0
- package/dist/dashboard/code-intelligence-data.d.ts.map +1 -0
- package/dist/dashboard/code-intelligence-data.js +329 -0
- package/dist/dashboard/dashboard-api-server.d.ts.map +1 -1
- package/dist/dashboard/dashboard-api-server.js +256 -2
- package/dist/dashboard/knowledge-ask.d.ts +4 -0
- package/dist/dashboard/knowledge-ask.d.ts.map +1 -0
- package/dist/dashboard/knowledge-ask.js +112 -0
- package/dist/env/load-dotenv.d.ts +15 -0
- package/dist/env/load-dotenv.d.ts.map +1 -0
- package/dist/env/load-dotenv.js +70 -0
- package/dist/export/claude-commands-export.d.ts +60 -0
- package/dist/export/claude-commands-export.d.ts.map +1 -0
- package/dist/export/claude-commands-export.js +276 -0
- package/dist/export/export-formats.d.ts +1 -1
- package/dist/export/export-formats.d.ts.map +1 -1
- package/dist/export/export-formats.js +139 -12
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/init/init-templates.d.ts.map +1 -1
- package/dist/init/init-templates.js +133 -113
- package/dist/init/paths-advisory.d.ts +20 -0
- package/dist/init/paths-advisory.d.ts.map +1 -0
- package/dist/init/paths-advisory.js +88 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +331 -17
- package/dist/output/ccr-store-config.d.ts +18 -0
- package/dist/output/ccr-store-config.d.ts.map +1 -0
- package/dist/output/ccr-store-config.js +41 -0
- package/dist/output/format-output.d.ts.map +1 -1
- package/dist/output/format-output.js +6 -1
- package/dist/output/output-compression.d.ts +15 -0
- package/dist/output/output-compression.d.ts.map +1 -0
- package/dist/output/output-compression.js +60 -0
- package/dist/output/resolve-compress-type.d.ts +22 -0
- package/dist/output/resolve-compress-type.d.ts.map +1 -0
- package/dist/output/resolve-compress-type.js +21 -0
- package/dist/output/watch-loop.d.ts +9 -1
- package/dist/output/watch-loop.d.ts.map +1 -1
- package/dist/output/watch-loop.js +13 -3
- package/dist/schemas/json-schemas.d.ts +384 -36
- package/dist/schemas/json-schemas.d.ts.map +1 -1
- package/dist/schemas/json-schemas.js +247 -36
- package/dist/surface/profiles.d.ts.map +1 -1
- package/dist/surface/profiles.js +54 -9
- package/dist/surface/surface-config-writer.d.ts.map +1 -1
- package/dist/surface/surface-config-writer.js +23 -11
- package/dist/validation/run-validation-loop.d.ts.map +1 -1
- package/dist/validation/run-validation-loop.js +5 -1
- package/package.json +35 -21
- package/dist/commands/plugin.command.d.ts +0 -11
- package/dist/commands/plugin.command.d.ts.map +0 -1
- package/dist/commands/plugin.command.js +0 -394
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import * as nodePath from 'node:path';
|
|
3
|
+
import { GraphQueryApi, GraphStore } from '@shrkcrft/graph';
|
|
4
|
+
import { flagBool, resolveCwd } from "../command-registry.js";
|
|
5
|
+
import { asJson, header } from "../output/format-output.js";
|
|
6
|
+
/**
|
|
7
|
+
* `shrk move-plan <source> <target>` — emit a structured plan for
|
|
8
|
+
* moving a source file to a new location. Read-only: NEVER moves
|
|
9
|
+
* anything itself, never rewrites imports. The agent (or a human)
|
|
10
|
+
* uses the plan to do the actual work.
|
|
11
|
+
*
|
|
12
|
+
* Covers:
|
|
13
|
+
* - graph-traced importer rewrites (relative + package-name imports)
|
|
14
|
+
* - exports to revisit if the source was re-exported from an index
|
|
15
|
+
* - cross-package warnings (layer-order risks)
|
|
16
|
+
* - a rollback plan that's the literal reverse of the move
|
|
17
|
+
* - validation commands to run after applying
|
|
18
|
+
*
|
|
19
|
+
* Limitations:
|
|
20
|
+
* - single-file only. Directory moves can be approximated by
|
|
21
|
+
* listing the directory and running this command per file, or
|
|
22
|
+
* wait for a `--folder` follow-up.
|
|
23
|
+
* - `from '@shrkcrft/pkg/sub'` deep-import rewrites are best-effort;
|
|
24
|
+
* ambiguous cases get reported as `review-manually`.
|
|
25
|
+
*/
|
|
26
|
+
export const movePlanCommand = {
|
|
27
|
+
name: 'move-plan',
|
|
28
|
+
description: 'Plan a file move: graph-traced importer rewrites, export touch-ups, cross-package warnings. Read-only.',
|
|
29
|
+
usage: 'shrk move-plan <source-file> <target-file> [--json]',
|
|
30
|
+
async run(args) {
|
|
31
|
+
const sourceArg = args.positional[0]?.trim();
|
|
32
|
+
const targetArg = args.positional[1]?.trim();
|
|
33
|
+
if (!sourceArg || !targetArg) {
|
|
34
|
+
process.stderr.write('Usage: shrk move-plan <source-file> <target-file> [--json]\n');
|
|
35
|
+
return 2;
|
|
36
|
+
}
|
|
37
|
+
const cwd = resolveCwd(args);
|
|
38
|
+
const json = flagBool(args, 'json');
|
|
39
|
+
const sourceRel = relativize(cwd, sourceArg);
|
|
40
|
+
const targetRel = relativize(cwd, targetArg);
|
|
41
|
+
if (!existsSync(nodePath.join(cwd, sourceRel))) {
|
|
42
|
+
process.stderr.write(`Source file does not exist: ${sourceRel}\n`);
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
if (existsSync(nodePath.join(cwd, targetRel))) {
|
|
46
|
+
process.stderr.write(`Target already exists (won't propose moving on top of it): ${targetRel}\n`);
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
const store = new GraphStore(cwd);
|
|
50
|
+
if (!store.exists()) {
|
|
51
|
+
process.stderr.write('No SharkCraft graph found. Run `shrk graph index` so move-plan can trace importers.\n');
|
|
52
|
+
return 1;
|
|
53
|
+
}
|
|
54
|
+
const api = GraphQueryApi.fromStore(cwd);
|
|
55
|
+
const sourcePackage = resolveOwningPackage(cwd, sourceRel);
|
|
56
|
+
const targetPackage = resolveOwningPackage(cwd, targetRel);
|
|
57
|
+
const crossesPackages = sourcePackage?.name !== targetPackage?.name;
|
|
58
|
+
const importerNodes = (() => {
|
|
59
|
+
const fileNode = api.findFile(sourceRel);
|
|
60
|
+
if (!fileNode)
|
|
61
|
+
return [];
|
|
62
|
+
return api.importersOf(fileNode.id)
|
|
63
|
+
.map((n) => n.path)
|
|
64
|
+
.filter((p) => typeof p === 'string' && p.length > 0);
|
|
65
|
+
})();
|
|
66
|
+
const importsToRewrite = [];
|
|
67
|
+
const exportsToUpdate = [];
|
|
68
|
+
for (const importerPath of importerNodes) {
|
|
69
|
+
const abs = nodePath.join(cwd, importerPath);
|
|
70
|
+
if (!existsSync(abs))
|
|
71
|
+
continue;
|
|
72
|
+
let body;
|
|
73
|
+
try {
|
|
74
|
+
body = readFileSync(abs, 'utf8');
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const rewrites = computeRewritesForFile({
|
|
80
|
+
importerPath,
|
|
81
|
+
importerBody: body,
|
|
82
|
+
sourceRel,
|
|
83
|
+
targetRel,
|
|
84
|
+
sourcePackage,
|
|
85
|
+
targetPackage,
|
|
86
|
+
});
|
|
87
|
+
importsToRewrite.push(...rewrites);
|
|
88
|
+
// If the importer is `<pkg>/src/index.ts` and re-exports the source,
|
|
89
|
+
// it likely needs to drop / move that export.
|
|
90
|
+
if (/(^|\/)index\.[jt]sx?$/.test(importerPath) && body.includes(sourceBaseSpecifier(sourceRel))) {
|
|
91
|
+
exportsToUpdate.push({
|
|
92
|
+
file: importerPath,
|
|
93
|
+
action: 'review-manually',
|
|
94
|
+
reason: `index file re-exports something from "${sourceRel}"; relocate the re-export to point at "${targetRel}" or drop it.`,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Also check if the source file itself is referenced from its own
|
|
99
|
+
// package's index.ts.
|
|
100
|
+
if (sourcePackage) {
|
|
101
|
+
const sourceIndex = nodePath.join(cwd, sourcePackage.dir, 'src/index.ts');
|
|
102
|
+
const sourceIndexRel = nodePath.relative(cwd, sourceIndex);
|
|
103
|
+
const alreadyFlagged = exportsToUpdate.some((e) => e.file === sourceIndexRel);
|
|
104
|
+
if (!alreadyFlagged && existsSync(sourceIndex)) {
|
|
105
|
+
try {
|
|
106
|
+
const body = readFileSync(sourceIndex, 'utf8');
|
|
107
|
+
if (body.includes(sourceBaseSpecifier(sourceRel))) {
|
|
108
|
+
exportsToUpdate.push({
|
|
109
|
+
file: sourceIndexRel,
|
|
110
|
+
action: 'edit',
|
|
111
|
+
reason: crossesPackages
|
|
112
|
+
? `index re-exports the moved file; drop the export (the file now lives in another package) or replace with a forwarding re-export.`
|
|
113
|
+
: `index re-exports the moved file; update the relative path to point at the new location.`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
/* ignore */
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const affectedPackages = unique([
|
|
123
|
+
...importerNodes.map((p) => resolveOwningPackage(cwd, p)?.name ?? null),
|
|
124
|
+
sourcePackage?.name ?? null,
|
|
125
|
+
targetPackage?.name ?? null,
|
|
126
|
+
].filter((n) => typeof n === 'string'));
|
|
127
|
+
const risks = [];
|
|
128
|
+
if (crossesPackages) {
|
|
129
|
+
risks.push(`Cross-package move: ${sourcePackage?.name ?? '(no package)'} → ${targetPackage?.name ?? '(no package)'}. Run \`shrk check boundaries\` after the move to confirm the new home is layered correctly.`);
|
|
130
|
+
}
|
|
131
|
+
if (importerNodes.length > 30) {
|
|
132
|
+
risks.push(`${importerNodes.length} importer(s) — large blast radius. Consider scripting the rewrites or moving in smaller chunks.`);
|
|
133
|
+
}
|
|
134
|
+
if (exportsToUpdate.some((e) => e.action === 'review-manually')) {
|
|
135
|
+
risks.push('Some index re-exports were flagged for manual review — automatic rewriting could lose intent.');
|
|
136
|
+
}
|
|
137
|
+
const movePlan = [
|
|
138
|
+
{
|
|
139
|
+
order: 1,
|
|
140
|
+
title: 'Move the file',
|
|
141
|
+
details: `git mv "${sourceRel}" "${targetRel}" (then verify content unchanged)`,
|
|
142
|
+
},
|
|
143
|
+
...importsToRewrite.map((rw, i) => ({
|
|
144
|
+
order: 2 + i,
|
|
145
|
+
title: `Rewrite import in ${rw.file}`,
|
|
146
|
+
details: `Replace \`from '${rw.fromImport}'\` with \`from '${rw.toImport}'\`. Reason: ${rw.reason}`,
|
|
147
|
+
})),
|
|
148
|
+
...(exportsToUpdate.length > 0
|
|
149
|
+
? [
|
|
150
|
+
{
|
|
151
|
+
order: 2 + importsToRewrite.length,
|
|
152
|
+
title: 'Reconcile index re-exports',
|
|
153
|
+
details: exportsToUpdate
|
|
154
|
+
.map((e) => `${e.file} (${e.action}): ${e.reason}`)
|
|
155
|
+
.join(' || '),
|
|
156
|
+
},
|
|
157
|
+
]
|
|
158
|
+
: []),
|
|
159
|
+
];
|
|
160
|
+
const rollbackPlan = [
|
|
161
|
+
{ order: 1, title: 'Restore file', details: `git mv "${targetRel}" "${sourceRel}"` },
|
|
162
|
+
{
|
|
163
|
+
order: 2,
|
|
164
|
+
title: 'Revert import rewrites',
|
|
165
|
+
details: importsToRewrite.length === 0
|
|
166
|
+
? 'No importers to revert.'
|
|
167
|
+
: `git restore ${unique(importsToRewrite.map((r) => r.file)).join(' ')}`,
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
const validationCommands = [
|
|
171
|
+
'bun x tsc -p tsconfig.base.json --noEmit',
|
|
172
|
+
'shrk check boundaries',
|
|
173
|
+
'shrk check imports',
|
|
174
|
+
'bun test',
|
|
175
|
+
];
|
|
176
|
+
const status = importsToRewrite.length === 0 && exportsToUpdate.length === 0
|
|
177
|
+
? 'no-importers'
|
|
178
|
+
: crossesPackages
|
|
179
|
+
? 'cross-package'
|
|
180
|
+
: 'in-package';
|
|
181
|
+
const handoff = status === 'no-importers'
|
|
182
|
+
? `Move is trivial — no importers reference "${sourceRel}". Just run the file move + validation.`
|
|
183
|
+
: status === 'cross-package'
|
|
184
|
+
? `Move crosses packages. Apply each importsToRewrite[] edit, then revisit exportsToUpdate[], then run validationCommands. Re-run \`shrk check boundaries\` to confirm layering.`
|
|
185
|
+
: `In-package move with ${importsToRewrite.length} importer rewrite(s). Safe but worth applying mechanically.`;
|
|
186
|
+
const report = {
|
|
187
|
+
schema: 'sharkcraft.move-plan/v1',
|
|
188
|
+
source: { path: sourceRel, package: sourcePackage?.name ?? null },
|
|
189
|
+
target: { path: targetRel, package: targetPackage?.name ?? null },
|
|
190
|
+
crossesPackages,
|
|
191
|
+
movePlan,
|
|
192
|
+
importsToRewrite,
|
|
193
|
+
exportsToUpdate,
|
|
194
|
+
affectedPackages,
|
|
195
|
+
risks,
|
|
196
|
+
rollbackPlan,
|
|
197
|
+
validationCommands,
|
|
198
|
+
handoffForClaude: handoff,
|
|
199
|
+
};
|
|
200
|
+
if (json) {
|
|
201
|
+
process.stdout.write(asJson(report) + '\n');
|
|
202
|
+
return 0;
|
|
203
|
+
}
|
|
204
|
+
renderText(report);
|
|
205
|
+
return 0;
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
function relativize(cwd, raw) {
|
|
209
|
+
const cleaned = raw.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
210
|
+
if (nodePath.isAbsolute(cleaned)) {
|
|
211
|
+
return nodePath.relative(cwd, cleaned).replace(/\\/g, '/');
|
|
212
|
+
}
|
|
213
|
+
return cleaned;
|
|
214
|
+
}
|
|
215
|
+
const PACKAGE_CACHE = new Map();
|
|
216
|
+
function listPackages(cwd) {
|
|
217
|
+
const cached = PACKAGE_CACHE.get(cwd);
|
|
218
|
+
if (cached)
|
|
219
|
+
return cached;
|
|
220
|
+
const out = [];
|
|
221
|
+
for (const root of ['packages', 'libs', 'apps']) {
|
|
222
|
+
const rootAbs = nodePath.join(cwd, root);
|
|
223
|
+
if (!existsSync(rootAbs))
|
|
224
|
+
continue;
|
|
225
|
+
let entries;
|
|
226
|
+
try {
|
|
227
|
+
entries = readdirSync(rootAbs);
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
for (const entry of entries) {
|
|
233
|
+
const dir = nodePath.join(root, entry);
|
|
234
|
+
const pkgJson = nodePath.join(cwd, dir, 'package.json');
|
|
235
|
+
if (!existsSync(pkgJson))
|
|
236
|
+
continue;
|
|
237
|
+
try {
|
|
238
|
+
const parsed = JSON.parse(readFileSync(pkgJson, 'utf8'));
|
|
239
|
+
if (parsed.name)
|
|
240
|
+
out.push({ name: parsed.name, dir });
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// skip
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
PACKAGE_CACHE.set(cwd, out);
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
function resolveOwningPackage(cwd, fileRel) {
|
|
251
|
+
const packages = listPackages(cwd);
|
|
252
|
+
let best = null;
|
|
253
|
+
for (const p of packages) {
|
|
254
|
+
const dirSlash = p.dir.replace(/\\/g, '/') + '/';
|
|
255
|
+
if (fileRel.startsWith(dirSlash)) {
|
|
256
|
+
if (!best || p.dir.length > best.dir.length)
|
|
257
|
+
best = p;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return best;
|
|
261
|
+
}
|
|
262
|
+
function sourceBaseSpecifier(sourceRel) {
|
|
263
|
+
// The plain basename without extension — usable as a fuzzy substring
|
|
264
|
+
// match against `from '../foo'` re-exports.
|
|
265
|
+
const ext = nodePath.extname(sourceRel);
|
|
266
|
+
return nodePath.basename(sourceRel, ext);
|
|
267
|
+
}
|
|
268
|
+
function computeRewritesForFile(input) {
|
|
269
|
+
const out = [];
|
|
270
|
+
const seen = new Set();
|
|
271
|
+
const importerDir = nodePath.dirname(input.importerPath);
|
|
272
|
+
const expectedRelativeBefore = toRelativeSpecifier(importerDir, input.sourceRel);
|
|
273
|
+
const expectedRelativeAfter = toRelativeSpecifier(importerDir, input.targetRel);
|
|
274
|
+
// Pattern 1: relative imports that resolve to the source file.
|
|
275
|
+
// Try both with-extension and without-extension forms.
|
|
276
|
+
const candidates = [expectedRelativeBefore, stripJsTsExt(expectedRelativeBefore)];
|
|
277
|
+
for (const cand of candidates) {
|
|
278
|
+
if (cand.length === 0)
|
|
279
|
+
continue;
|
|
280
|
+
if (!input.importerBody.includes(`'${cand}'`) && !input.importerBody.includes(`"${cand}"`))
|
|
281
|
+
continue;
|
|
282
|
+
const targetCand = stripJsTsExt(expectedRelativeAfter);
|
|
283
|
+
const key = `rel:${cand}`;
|
|
284
|
+
if (seen.has(key))
|
|
285
|
+
continue;
|
|
286
|
+
seen.add(key);
|
|
287
|
+
out.push({
|
|
288
|
+
file: input.importerPath,
|
|
289
|
+
fromImport: cand,
|
|
290
|
+
toImport: targetCand,
|
|
291
|
+
reason: 'relative-path rewrite (importer-relative)',
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
// Pattern 2: package-name imports `@shrkcrft/xxx` when the source is
|
|
295
|
+
// inside that package and the target is in a different one.
|
|
296
|
+
if (input.sourcePackage && input.targetPackage && input.sourcePackage.name !== input.targetPackage.name) {
|
|
297
|
+
const fromSpec = input.sourcePackage.name;
|
|
298
|
+
const toSpec = input.targetPackage.name;
|
|
299
|
+
if ((input.importerBody.includes(`'${fromSpec}'`) || input.importerBody.includes(`"${fromSpec}"`)) &&
|
|
300
|
+
// and the package-name import is actually exposing the moved symbol
|
|
301
|
+
// (we can't know for sure without re-resolving — flag as review).
|
|
302
|
+
input.importerPath !== fromIndexPath(input.sourcePackage)) {
|
|
303
|
+
const key = `pkg:${fromSpec}->${toSpec}`;
|
|
304
|
+
if (!seen.has(key)) {
|
|
305
|
+
seen.add(key);
|
|
306
|
+
out.push({
|
|
307
|
+
file: input.importerPath,
|
|
308
|
+
fromImport: fromSpec,
|
|
309
|
+
toImport: toSpec,
|
|
310
|
+
reason: 'package-name import: importer likely consumes the symbol via the old package. Verify the target package re-exports the symbol from its public index before applying.',
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return out;
|
|
316
|
+
}
|
|
317
|
+
function fromIndexPath(pkg) {
|
|
318
|
+
return nodePath.join(pkg.dir, 'src/index.ts');
|
|
319
|
+
}
|
|
320
|
+
function toRelativeSpecifier(fromDir, toRel) {
|
|
321
|
+
const rel = nodePath.relative(fromDir, toRel).replace(/\\/g, '/');
|
|
322
|
+
if (!rel.startsWith('.'))
|
|
323
|
+
return `./${rel}`;
|
|
324
|
+
return rel;
|
|
325
|
+
}
|
|
326
|
+
function stripJsTsExt(p) {
|
|
327
|
+
return p.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, '');
|
|
328
|
+
}
|
|
329
|
+
function unique(items) {
|
|
330
|
+
return [...new Set(items)];
|
|
331
|
+
}
|
|
332
|
+
function renderText(r) {
|
|
333
|
+
process.stdout.write(header(`move-plan: ${r.source.path} → ${r.target.path}${r.crossesPackages ? ' (cross-package)' : ''}`));
|
|
334
|
+
process.stdout.write(`affected packages: ${r.affectedPackages.join(', ') || '(none)'}\n\n`);
|
|
335
|
+
process.stdout.write(`move plan (${r.movePlan.length} step(s)):\n`);
|
|
336
|
+
for (const s of r.movePlan) {
|
|
337
|
+
process.stdout.write(` ${s.order}. ${s.title} — ${s.details}\n`);
|
|
338
|
+
}
|
|
339
|
+
if (r.importsToRewrite.length > 0) {
|
|
340
|
+
process.stdout.write(`\nimport rewrites (${r.importsToRewrite.length}):\n`);
|
|
341
|
+
for (const rw of r.importsToRewrite) {
|
|
342
|
+
process.stdout.write(` ${rw.file}\n from '${rw.fromImport}' → to '${rw.toImport}'\n (${rw.reason})\n`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (r.exportsToUpdate.length > 0) {
|
|
346
|
+
process.stdout.write('\nexports to update:\n');
|
|
347
|
+
for (const e of r.exportsToUpdate) {
|
|
348
|
+
process.stdout.write(` ${e.file} [${e.action}] — ${e.reason}\n`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (r.risks.length > 0) {
|
|
352
|
+
process.stdout.write('\nrisks:\n');
|
|
353
|
+
for (const r2 of r.risks)
|
|
354
|
+
process.stdout.write(` - ${r2}\n`);
|
|
355
|
+
}
|
|
356
|
+
process.stdout.write('\nvalidation commands:\n');
|
|
357
|
+
for (const c of r.validationCommands)
|
|
358
|
+
process.stdout.write(` $ ${c}\n`);
|
|
359
|
+
process.stdout.write(`\nhandoff: ${r.handoffForClaude}\n`);
|
|
360
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ICommandHandler } from '../command-registry.js';
|
|
2
|
-
export type PackKind = 'generic' | 'framework' | 'architecture' | 'enterprise'
|
|
2
|
+
export type PackKind = 'generic' | 'framework' | 'architecture' | 'enterprise';
|
|
3
3
|
interface IScaffoldFile {
|
|
4
4
|
/** Path relative to the new pack root. */
|
|
5
5
|
relativePath: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packs-new.d.ts","sourceRoot":"","sources":["../../src/commands/packs-new.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"packs-new.d.ts","sourceRoot":"","sources":["../../src/commands/packs-new.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAIhC,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,cAAc,GAAG,YAAY,CAAC;AAS/E,UAAU,aAAa;IACrB,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,kDAAkD;AAClD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,GAAG,mBAAmB,CAyG/E;AA0SD,eAAO,MAAM,eAAe,EAAE,eAoG7B,CAAC;AAUF,eAAO,MAAM,gBAAgB,EAAE,eAqI9B,CAAC"}
|
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
-
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
-
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
-
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
-
});
|
|
6
|
-
}
|
|
7
|
-
return path;
|
|
8
|
-
};
|
|
9
1
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
10
2
|
import * as nodePath from 'node:path';
|
|
11
3
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
12
4
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
5
|
+
import { importModuleViaLoader } from '@shrkcrft/core';
|
|
13
6
|
const VALID_KINDS = new Set([
|
|
14
7
|
'generic',
|
|
15
8
|
'framework',
|
|
16
9
|
'architecture',
|
|
17
10
|
'enterprise',
|
|
18
|
-
'platform-adopter',
|
|
19
11
|
]);
|
|
20
12
|
/** Pure: compute the file set to write. No IO. */
|
|
21
13
|
export function planPackScaffold(input) {
|
|
@@ -108,12 +100,6 @@ export function planPackScaffold(input) {
|
|
|
108
100
|
body: renderBoundariesAsset(),
|
|
109
101
|
});
|
|
110
102
|
}
|
|
111
|
-
if (input.kind === 'platform-adopter' || input.withExamples) {
|
|
112
|
-
files.push({
|
|
113
|
-
relativePath: 'src/assets/contracts/plugin-contract.example.ts',
|
|
114
|
-
body: renderPluginContractExample(),
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
103
|
if (input.kind === 'enterprise') {
|
|
118
104
|
files.push({
|
|
119
105
|
relativePath: 'docs/review-workflow.md',
|
|
@@ -239,12 +225,6 @@ function renderKnowledgeAsset(input) {
|
|
|
239
225
|
['framework.overview', 'Framework overview', 'High', 'What this framework is for.'],
|
|
240
226
|
]);
|
|
241
227
|
}
|
|
242
|
-
if (input.kind === 'platform-adopter') {
|
|
243
|
-
return knowledgeBody([
|
|
244
|
-
['platform.policy', 'Policy capability', 'Medium', 'Describe the policy/capability model your platform exposes.'],
|
|
245
|
-
['platform.adapter', 'Adapter contract', 'Medium', 'Describe the adapter contract your platform expects.'],
|
|
246
|
-
]);
|
|
247
|
-
}
|
|
248
228
|
return knowledgeBody([
|
|
249
229
|
['pack.overview', 'Pack overview', 'Medium', 'Short overview of what this pack contributes.'],
|
|
250
230
|
]);
|
|
@@ -357,17 +337,6 @@ function renderBoundariesAsset() {
|
|
|
357
337
|
``,
|
|
358
338
|
].join('\n');
|
|
359
339
|
}
|
|
360
|
-
function renderPluginContractExample() {
|
|
361
|
-
return [
|
|
362
|
-
`// Example: an adapter-style plugin contract.`,
|
|
363
|
-
`// Replace with the actual interface your platform expects.`,
|
|
364
|
-
`export interface IPluginCapability {`,
|
|
365
|
-
` id: string;`,
|
|
366
|
-
` invoke(input: unknown): Promise<unknown>;`,
|
|
367
|
-
`}`,
|
|
368
|
-
``,
|
|
369
|
-
].join('\n');
|
|
370
|
-
}
|
|
371
340
|
function renderEnterpriseReviewDocs() {
|
|
372
341
|
return [
|
|
373
342
|
`# Review workflow`,
|
|
@@ -414,11 +383,11 @@ function renderDocsOverview(input, fullName) {
|
|
|
414
383
|
export const packsNewCommand = {
|
|
415
384
|
name: 'new',
|
|
416
385
|
description: 'Scaffold a new SharkCraft pack package (rules / paths / templates / pipelines / presets / boundaries). Dry-run by default — pass --write to materialize. No install, no publish, no overwrite without --force.',
|
|
417
|
-
usage: 'shrk [--cwd <dir>] packs new <name> [--scope @org] [--preset <id>] [--kind generic|framework|architecture|enterprise
|
|
386
|
+
usage: 'shrk [--cwd <dir>] packs new <name> [--scope @org] [--preset <id>] [--kind generic|framework|architecture|enterprise] [--with-examples] [--write] [--force] [--json]',
|
|
418
387
|
async run(args) {
|
|
419
388
|
const name = args.positional[0];
|
|
420
389
|
if (!name) {
|
|
421
|
-
process.stderr.write('Usage: shrk packs new <name> [--scope @org] [--preset <id>] [--kind generic|framework|architecture|enterprise
|
|
390
|
+
process.stderr.write('Usage: shrk packs new <name> [--scope @org] [--preset <id>] [--kind generic|framework|architecture|enterprise] [--with-examples] [--write]\n');
|
|
422
391
|
return 2;
|
|
423
392
|
}
|
|
424
393
|
const cwd = resolveCwd(args);
|
|
@@ -645,7 +614,7 @@ async function runRuntimePackTest(input) {
|
|
|
645
614
|
if (existsSync(entry)) {
|
|
646
615
|
try {
|
|
647
616
|
const { pathToFileURL } = await import('node:url');
|
|
648
|
-
const mod = (await
|
|
617
|
+
const mod = (await importModuleViaLoader(entry));
|
|
649
618
|
const value = mod.default ?? mod;
|
|
650
619
|
const shape = describeShape(value);
|
|
651
620
|
modules.push({
|
|
@@ -692,7 +661,7 @@ async function runRuntimePackTest(input) {
|
|
|
692
661
|
continue;
|
|
693
662
|
try {
|
|
694
663
|
const { pathToFileURL } = await import('node:url');
|
|
695
|
-
const mod = (await
|
|
664
|
+
const mod = (await importModuleViaLoader(full));
|
|
696
665
|
const value = mod.default;
|
|
697
666
|
const arr = Array.isArray(value) ? value : null;
|
|
698
667
|
modules.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packs.command.d.ts","sourceRoot":"","sources":["../../src/commands/packs.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"packs.command.d.ts","sourceRoot":"","sources":["../../src/commands/packs.command.ts"],"names":[],"mappings":"AAoBA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAQhC,eAAO,MAAM,yBAAyB,EAAE,eAoCvC,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,eAsGzC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eA+CnC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,eAuD9B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,eAsE7B,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,eAqDjC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,eAyFhC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,eAqEhC,CAAC;AA4DF,eAAO,MAAM,gBAAgB,EAAE,eA+K9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eAmCtC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,eAmFhC,CAAC;AAMF,eAAO,MAAM,qBAAqB,EAAE,eA4BnC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,eA+E/B,CAAC"}
|
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
-
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
-
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
-
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
-
});
|
|
6
|
-
}
|
|
7
|
-
return path;
|
|
8
|
-
};
|
|
9
1
|
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
10
2
|
import * as nodePath from 'node:path';
|
|
11
|
-
import { pathToFileURL } from 'node:url';
|
|
12
3
|
import { buildPackDoctorReport, buildPackSignatureStatusReport, checkPackSymbolCompat, explainPackSignatureStatus, inspectSharkcraft, mergePackReleaseChecks, runPackReleaseCheck, runPackReleaseChecksForReport, } from '@shrkcrft/inspector';
|
|
13
4
|
import { PACK_SECRET_ENV, signPackManifest, validatePackManifest, verifyPackManifest, } from '@shrkcrft/plugin-api';
|
|
14
5
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
15
6
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
7
|
+
import { importModuleViaLoader } from '@shrkcrft/core';
|
|
16
8
|
function statusLabel(valid) {
|
|
17
9
|
return valid ? 'OK ' : 'INVALID';
|
|
18
10
|
}
|
|
@@ -553,7 +545,7 @@ async function loadManifestFromPath(manifestPath) {
|
|
|
553
545
|
if (manifestPath.endsWith('.json')) {
|
|
554
546
|
return JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
555
547
|
}
|
|
556
|
-
const mod = (await
|
|
548
|
+
const mod = (await importModuleViaLoader(manifestPath));
|
|
557
549
|
return (mod.default ?? mod);
|
|
558
550
|
}
|
|
559
551
|
export const packsSignCommand = {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ICommandHandler } from '../command-registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* `shrk plan-context` — produce a deterministic, token-budgeted context
|
|
4
|
+
* pack for an AI coding agent. Reads the code graph + (optionally) the
|
|
5
|
+
* rule-graph bridge.
|
|
6
|
+
*
|
|
7
|
+
* Read-only. The pack is whatever the planner decides; the agent uses
|
|
8
|
+
* it as the starting point for its conversation.
|
|
9
|
+
*/
|
|
10
|
+
export declare const planContextCommand: ICommandHandler;
|
|
11
|
+
//# sourceMappingURL=plan-context.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/plan-context.command.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,eAsEhC,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { planContext } from '@shrkcrft/context-planner';
|
|
2
|
+
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
3
|
+
import { asJson, header, kv } from "../output/format-output.js";
|
|
4
|
+
/**
|
|
5
|
+
* `shrk plan-context` — produce a deterministic, token-budgeted context
|
|
6
|
+
* pack for an AI coding agent. Reads the code graph + (optionally) the
|
|
7
|
+
* rule-graph bridge.
|
|
8
|
+
*
|
|
9
|
+
* Read-only. The pack is whatever the planner decides; the agent uses
|
|
10
|
+
* it as the starting point for its conversation.
|
|
11
|
+
*/
|
|
12
|
+
export const planContextCommand = {
|
|
13
|
+
name: 'plan-context',
|
|
14
|
+
description: 'Produce a deterministic, token-budgeted context pack (`sharkcraft.context-pack/v1`) for an AI agent: ranked files, likely tests, and (shallow) surfaced risks / do-not-touch zones. Rules/paths/templates require the rule-graph bridge (`shrk rule-graph index`); the pack reports per-field `coverage` so empty sections are distinguishable from not-computed ones.',
|
|
15
|
+
usage: 'shrk plan-context "<task>" [--budget N] [--max-files N] [--hint-file <path>] [--hint-package <prefix>] [--json]',
|
|
16
|
+
async run(args) {
|
|
17
|
+
const cwd = resolveCwd(args);
|
|
18
|
+
const wantJson = flagBool(args, 'json');
|
|
19
|
+
const task = args.positional[0];
|
|
20
|
+
if (!task) {
|
|
21
|
+
process.stderr.write(this.usage + '\n');
|
|
22
|
+
return 2;
|
|
23
|
+
}
|
|
24
|
+
const budget = Number(flagString(args, 'budget') ?? '8000');
|
|
25
|
+
const maxFiles = Number(flagString(args, 'max-files') ?? '30');
|
|
26
|
+
const hintedFiles = collectMulti(args, 'hint-file');
|
|
27
|
+
const hintedPackages = collectMulti(args, 'hint-package');
|
|
28
|
+
const pack = planContext({
|
|
29
|
+
projectRoot: cwd,
|
|
30
|
+
task,
|
|
31
|
+
budgetTokens: budget,
|
|
32
|
+
maxFiles,
|
|
33
|
+
...(hintedFiles.length > 0 ? { hintedFiles } : {}),
|
|
34
|
+
...(hintedPackages.length > 0 ? { hintedPackages } : {}),
|
|
35
|
+
});
|
|
36
|
+
if (wantJson) {
|
|
37
|
+
process.stdout.write(asJson(pack) + '\n');
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
process.stdout.write(header(`Context pack: ${task}`));
|
|
41
|
+
process.stdout.write(kv('intent', pack.intent) + '\n');
|
|
42
|
+
process.stdout.write(kv('files', String(pack.files.length)) + '\n');
|
|
43
|
+
// Distinguish "computed, none apply" from "not computed". When the bridge
|
|
44
|
+
// is missing, rules/paths/templates are omitted, not empty — say so rather
|
|
45
|
+
// than printing a misleading `0`.
|
|
46
|
+
if (pack.coverage.rulesComputed) {
|
|
47
|
+
process.stdout.write(kv('rules', String(pack.rules.length)) + '\n');
|
|
48
|
+
process.stdout.write(kv('paths', String(pack.paths.length)) + '\n');
|
|
49
|
+
process.stdout.write(kv('templates', String(pack.templates.length)) + '\n');
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
process.stdout.write(kv('rules/paths/templates', 'not computed (run `shrk rule-graph index`)') + '\n');
|
|
53
|
+
}
|
|
54
|
+
process.stdout.write(kv('tests', String(pack.tests.length)) + '\n');
|
|
55
|
+
process.stdout.write(kv('risks', pack.coverage.risksComputed ? String(pack.risks.length) : 'not computed') + '\n');
|
|
56
|
+
process.stdout.write(kv('budget', `${pack.budget.used}/${pack.budget.requested} tokens` + (pack.budget.truncated ? ' (truncated)' : '')) + '\n');
|
|
57
|
+
if (pack.files.length > 0) {
|
|
58
|
+
process.stdout.write('\nTop files:\n');
|
|
59
|
+
for (const f of pack.files.slice(0, 15)) {
|
|
60
|
+
const reason = f.reasons[0] ? ` [${f.reasons[0]}]` : '';
|
|
61
|
+
process.stdout.write(` ${f.score.toFixed(2)} ${f.path}${reason}\n`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (pack.tests.length > 0) {
|
|
65
|
+
process.stdout.write('\nLikely tests:\n');
|
|
66
|
+
for (const t of pack.tests.slice(0, 5))
|
|
67
|
+
process.stdout.write(` ${t}\n`);
|
|
68
|
+
}
|
|
69
|
+
if (pack.risks.length > 0) {
|
|
70
|
+
process.stdout.write('\nRisks:\n');
|
|
71
|
+
for (const r of pack.risks)
|
|
72
|
+
process.stdout.write(` • [${r.kind}] ${r.label}\n`);
|
|
73
|
+
}
|
|
74
|
+
for (const d of pack.diagnostics.slice(0, 5))
|
|
75
|
+
process.stdout.write(`! ${d}\n`);
|
|
76
|
+
return 0;
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
function collectMulti(args, name) {
|
|
80
|
+
const list = args.multiFlags.get(name);
|
|
81
|
+
if (list)
|
|
82
|
+
return [...list];
|
|
83
|
+
const single = flagString(args, name);
|
|
84
|
+
return single ? [single] : [];
|
|
85
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preflight.command.d.ts","sourceRoot":"","sources":["../../src/commands/preflight.command.ts"],"names":[],"mappings":"AAoBA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"preflight.command.d.ts","sourceRoot":"","sources":["../../src/commands/preflight.command.ts"],"names":[],"mappings":"AAoBA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAiBhC,eAAO,MAAM,gBAAgB,EAAE,eAoG9B,CAAC"}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { planChangedPreflight, PreflightAction, PreflightProfile, renderChangedPreflightText, resolveChangedFiles, } from '@shrkcrft/inspector';
|
|
15
15
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
16
|
+
import { loadProjectConfig } from '@shrkcrft/config';
|
|
16
17
|
import { asJson, header } from "../output/format-output.js";
|
|
17
18
|
function parseProfile(value) {
|
|
18
19
|
if (value === 'quick')
|
|
@@ -55,10 +56,24 @@ export const preflightCommand = {
|
|
|
55
56
|
resolveOpts.includeWorktree = true;
|
|
56
57
|
}
|
|
57
58
|
const changed = resolveChangedFiles(resolveOpts);
|
|
59
|
+
// Ground the test/typecheck gates in the project's declared verification
|
|
60
|
+
// commands when present, rather than the generic `bun test` / tsc defaults.
|
|
61
|
+
// Best-effort: a repo without sharkcraft/ config keeps the defaults.
|
|
62
|
+
let testCommand;
|
|
63
|
+
let typecheckCommand;
|
|
64
|
+
const loaded = await loadProjectConfig(cwd);
|
|
65
|
+
if (loaded.ok) {
|
|
66
|
+
const vcs = loaded.value.config.verificationCommands ?? [];
|
|
67
|
+
const pick = (...ids) => vcs.find((v) => ids.includes(v.id))?.command;
|
|
68
|
+
testCommand = pick('test', 'tests', 'unit', 'unit-tests');
|
|
69
|
+
typecheckCommand = pick('typecheck', 'tsc', 'types', 'typecheck-all');
|
|
70
|
+
}
|
|
58
71
|
const plan = planChangedPreflight({
|
|
59
72
|
projectRoot: cwd,
|
|
60
73
|
changedFiles: changed.files,
|
|
61
74
|
profile,
|
|
75
|
+
...(testCommand ? { testCommand } : {}),
|
|
76
|
+
...(typecheckCommand ? { typecheckCommand } : {}),
|
|
62
77
|
});
|
|
63
78
|
if (flagBool(args, 'json')) {
|
|
64
79
|
process.stdout.write(asJson({ plan, scopeMode: changed.mode }) + '\n');
|