sdx-cli 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +266 -0
  3. package/bin/dev.js +11 -0
  4. package/bin/run.js +3 -0
  5. package/dist/commands/bootstrap/consumer.js +75 -0
  6. package/dist/commands/bootstrap/org.js +29 -0
  7. package/dist/commands/bootstrap/quick.js +82 -0
  8. package/dist/commands/codex/run.js +36 -0
  9. package/dist/commands/contracts/extract.js +22 -0
  10. package/dist/commands/docs/generate.js +22 -0
  11. package/dist/commands/handoff/draft.js +41 -0
  12. package/dist/commands/init.js +14 -0
  13. package/dist/commands/map/build.js +44 -0
  14. package/dist/commands/map/create.js +40 -0
  15. package/dist/commands/map/exclude.js +25 -0
  16. package/dist/commands/map/include.js +25 -0
  17. package/dist/commands/map/remove-override.js +25 -0
  18. package/dist/commands/map/status.js +30 -0
  19. package/dist/commands/migrate/artifacts.js +68 -0
  20. package/dist/commands/plan/review.js +60 -0
  21. package/dist/commands/prompt.js +62 -0
  22. package/dist/commands/publish/notices.js +98 -0
  23. package/dist/commands/publish/sync.js +67 -0
  24. package/dist/commands/publish/wiki.js +39 -0
  25. package/dist/commands/repo/add.js +29 -0
  26. package/dist/commands/repo/sync.js +30 -0
  27. package/dist/commands/service/propose.js +40 -0
  28. package/dist/commands/status.js +37 -0
  29. package/dist/commands/version.js +16 -0
  30. package/dist/index.js +10 -0
  31. package/dist/lib/artifactMigration.js +29 -0
  32. package/dist/lib/bootstrap.js +43 -0
  33. package/dist/lib/bootstrapConsumer.js +187 -0
  34. package/dist/lib/bootstrapQuick.js +27 -0
  35. package/dist/lib/codex.js +138 -0
  36. package/dist/lib/config.js +40 -0
  37. package/dist/lib/constants.js +26 -0
  38. package/dist/lib/contractChanges.js +347 -0
  39. package/dist/lib/contracts.js +93 -0
  40. package/dist/lib/db.js +41 -0
  41. package/dist/lib/docs.js +46 -0
  42. package/dist/lib/fileScan.js +34 -0
  43. package/dist/lib/fs.js +36 -0
  44. package/dist/lib/github.js +52 -0
  45. package/dist/lib/githubPublish.js +161 -0
  46. package/dist/lib/handoff.js +62 -0
  47. package/dist/lib/mapBuilder.js +182 -0
  48. package/dist/lib/paths.js +39 -0
  49. package/dist/lib/planReview.js +88 -0
  50. package/dist/lib/project.js +65 -0
  51. package/dist/lib/promptParser.js +88 -0
  52. package/dist/lib/publishContracts.js +876 -0
  53. package/dist/lib/repoRegistry.js +92 -0
  54. package/dist/lib/scope.js +110 -0
  55. package/dist/lib/serviceNoticePlan.js +130 -0
  56. package/dist/lib/serviceProposal.js +82 -0
  57. package/dist/lib/status.js +34 -0
  58. package/dist/lib/types.js +2 -0
  59. package/dist/lib/version.js +17 -0
  60. package/dist/lib/workflows.js +70 -0
  61. package/package.json +50 -0
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_path_1 = __importDefault(require("node:path"));
7
+ const core_1 = require("@oclif/core");
8
+ const contracts_1 = require("../../lib/contracts");
9
+ const fs_1 = require("../../lib/fs");
10
+ const project_1 = require("../../lib/project");
11
+ const repoRegistry_1 = require("../../lib/repoRegistry");
12
+ const scope_1 = require("../../lib/scope");
13
+ const serviceProposal_1 = require("../../lib/serviceProposal");
14
+ class ServiceProposeCommand extends core_1.Command {
15
+ static description = 'Draft a new service proposal based on map context and a brief file';
16
+ static flags = {
17
+ map: core_1.Flags.string({ required: true, description: 'Map identifier' }),
18
+ brief: core_1.Flags.string({ required: true, description: 'Path to service brief file' }),
19
+ };
20
+ async run() {
21
+ const { flags } = await this.parse(ServiceProposeCommand);
22
+ const context = (0, project_1.loadProject)(process.cwd());
23
+ const scope = (0, scope_1.loadScopeManifest)(flags.map, context.cwd);
24
+ const repoMap = new Map((0, repoRegistry_1.listAllRepos)(context.db).map((repo) => [repo.name, repo]));
25
+ const contracts = (0, contracts_1.extractContracts)(flags.map, scope, repoMap);
26
+ const proposal = (0, serviceProposal_1.proposeService)(flags.map, flags.brief, scope, contracts);
27
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
28
+ const outJsonPath = node_path_1.default.join(context.cwd, 'plans', `${stamp}-${flags.map}-service-proposal.json`);
29
+ const outMdPath = node_path_1.default.join(context.cwd, 'plans', `${stamp}-${flags.map}-service-proposal.md`);
30
+ (0, fs_1.writeJsonFile)(outJsonPath, proposal);
31
+ (0, fs_1.writeTextFile)(outMdPath, (0, serviceProposal_1.renderServiceProposalMarkdown)(proposal));
32
+ (0, project_1.recordRun)(context.db, 'service_propose', 'ok', flags.map, {
33
+ proposalPath: outMdPath,
34
+ serviceName: proposal.proposedServiceName,
35
+ });
36
+ context.db.close();
37
+ this.log(`Service proposal written: ${outMdPath}`);
38
+ }
39
+ }
40
+ exports.default = ServiceProposeCommand;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_fs_1 = __importDefault(require("node:fs"));
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const core_1 = require("@oclif/core");
9
+ const project_1 = require("../lib/project");
10
+ const fs_1 = require("../lib/fs");
11
+ class StatusCommand extends core_1.Command {
12
+ static description = 'Show overall workspace status, map health, and recent outputs';
13
+ async run() {
14
+ const context = (0, project_1.loadProject)(process.cwd());
15
+ const repoCount = context.db.prepare('SELECT COUNT(*) AS count FROM repo_registry').get().count;
16
+ const runCount = context.db.prepare('SELECT COUNT(*) AS count FROM run_log').get().count;
17
+ this.log(`Initialized: yes`);
18
+ this.log(`Repositories tracked: ${repoCount}`);
19
+ this.log(`Runs logged: ${runCount}`);
20
+ const mapsDir = node_path_1.default.join(context.cwd, 'maps');
21
+ const mapIds = node_fs_1.default.existsSync(mapsDir)
22
+ ? node_fs_1.default
23
+ .readdirSync(mapsDir, { withFileTypes: true })
24
+ .filter((entry) => entry.isDirectory())
25
+ .map((entry) => entry.name)
26
+ : [];
27
+ this.log(`Maps: ${mapIds.length}`);
28
+ for (const mapId of mapIds) {
29
+ const mapDir = node_path_1.default.join(mapsDir, mapId);
30
+ const hasScope = (0, fs_1.fileExists)(node_path_1.default.join(mapDir, 'scope.json'));
31
+ const hasServiceMap = (0, fs_1.fileExists)(node_path_1.default.join(mapDir, 'service-map.json'));
32
+ this.log(`- ${mapId}: scope=${hasScope ? 'yes' : 'no'}, service-map=${hasServiceMap ? 'yes' : 'no'}`);
33
+ }
34
+ context.db.close();
35
+ }
36
+ }
37
+ exports.default = StatusCommand;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const core_1 = require("@oclif/core");
7
+ const package_json_1 = __importDefault(require("../../package.json"));
8
+ const constants_1 = require("../lib/constants");
9
+ class VersionCommand extends core_1.Command {
10
+ static description = 'Show CLI and artifact schema versions';
11
+ async run() {
12
+ this.log(`sdx-cli: ${package_json_1.default.version}`);
13
+ this.log(`artifact-schema: ${constants_1.SCHEMA_VERSION}`);
14
+ }
15
+ }
16
+ exports.default = VersionCommand;
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.run = run;
4
+ const core_1 = require("@oclif/core");
5
+ async function run() {
6
+ await (0, core_1.run)();
7
+ }
8
+ if (require.main === module) {
9
+ run().catch(require('@oclif/core/handle'));
10
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isManagedArtifactPath = isManagedArtifactPath;
7
+ exports.isManagedArtifactPayload = isManagedArtifactPayload;
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const MANAGED_ARTIFACT_PATTERNS = [
10
+ /^maps\/[^/]+\/(scope|service-map|contracts)\.json$/,
11
+ /^plans\/reviews\/.*\.json$/,
12
+ /^plans\/.*-service-proposal\.json$/,
13
+ /^handoffs\/.*\.json$/,
14
+ /^codex\/(context-packs|runs)\/.*\.json$/,
15
+ ];
16
+ function normalizeRelativePath(cwd, filePath) {
17
+ return node_path_1.default.relative(cwd, filePath).replaceAll(node_path_1.default.sep, '/');
18
+ }
19
+ function isManagedArtifactPath(cwd, filePath) {
20
+ const relative = normalizeRelativePath(cwd, filePath);
21
+ return MANAGED_ARTIFACT_PATTERNS.some((pattern) => pattern.test(relative));
22
+ }
23
+ function isManagedArtifactPayload(value) {
24
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
25
+ return false;
26
+ }
27
+ const candidate = value;
28
+ return typeof candidate.generatedAt === 'string' || typeof candidate.schemaVersion === 'string';
29
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createBootstrapStructure = createBootstrapStructure;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const fs_1 = require("./fs");
9
+ function createBootstrapStructure(org, repoName, cwd = process.cwd()) {
10
+ const workflowDir = node_path_1.default.join(cwd, '.github', 'workflows');
11
+ (0, fs_1.ensureDir)(workflowDir);
12
+ (0, fs_1.writeTextFile)(node_path_1.default.join(workflowDir, 'sdx-ci.yml'), [
13
+ 'name: sdx-ci',
14
+ '',
15
+ 'on:',
16
+ ' push:',
17
+ ' branches: [main]',
18
+ ' pull_request:',
19
+ '',
20
+ 'jobs:',
21
+ ' build-test:',
22
+ ' runs-on: ubuntu-latest',
23
+ ' steps:',
24
+ ' - uses: actions/checkout@v4',
25
+ ' - uses: actions/setup-node@v4',
26
+ ' with:',
27
+ ' node-version: 20',
28
+ ' - run: npm ci',
29
+ ' - run: npm run typecheck',
30
+ ' - run: npm run test',
31
+ ' - run: npm run build',
32
+ '',
33
+ ].join('\n'));
34
+ (0, fs_1.writeTextFile)(node_path_1.default.join(cwd, 'BOOTSTRAP.md'), [
35
+ '# SDX Bootstrap',
36
+ '',
37
+ `- Organization: ${org}`,
38
+ `- Design Repository: ${repoName}`,
39
+ '',
40
+ 'This repository is configured as a docs-first system-design intelligence workspace.',
41
+ '',
42
+ ].join('\n'));
43
+ }
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveConsumerTargetDir = resolveConsumerTargetDir;
7
+ exports.resolvePinnedVersion = resolvePinnedVersion;
8
+ exports.writeInstallManifest = writeInstallManifest;
9
+ exports.writePinnedWrapperScript = writePinnedWrapperScript;
10
+ exports.bootstrapConsumer = bootstrapConsumer;
11
+ const node_fs_1 = __importDefault(require("node:fs"));
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const bootstrap_1 = require("./bootstrap");
14
+ const constants_1 = require("./constants");
15
+ const fs_1 = require("./fs");
16
+ const github_1 = require("./github");
17
+ const mapBuilder_1 = require("./mapBuilder");
18
+ const project_1 = require("./project");
19
+ const repoRegistry_1 = require("./repoRegistry");
20
+ const scope_1 = require("./scope");
21
+ const config_1 = require("./config");
22
+ const version_1 = require("./version");
23
+ const semver = require('semver');
24
+ function resolveConsumerTargetDir(mode, designRepo, targetDir, cwd = process.cwd()) {
25
+ if (targetDir && targetDir.trim().length > 0) {
26
+ return node_path_1.default.resolve(cwd, targetDir);
27
+ }
28
+ if (mode === 'dedicated') {
29
+ return node_path_1.default.resolve(cwd, designRepo);
30
+ }
31
+ return node_path_1.default.resolve(cwd);
32
+ }
33
+ function resolvePinnedVersion(pin, currentVersion) {
34
+ const fallback = semver.valid(currentVersion);
35
+ if (!fallback) {
36
+ throw new Error(`Invalid package version: ${currentVersion}`);
37
+ }
38
+ if (!pin || pin.trim().length === 0) {
39
+ return fallback;
40
+ }
41
+ const normalized = semver.valid(pin);
42
+ if (!normalized) {
43
+ throw new Error(`Invalid --pin value '${pin}'. Use a concrete semver version (example: 0.1.0).`);
44
+ }
45
+ return normalized;
46
+ }
47
+ function writeInstallManifest(targetDir, org, designRepo, mode, pinnedVersion) {
48
+ const now = new Date().toISOString();
49
+ const payload = {
50
+ schemaVersion: constants_1.SCHEMA_VERSION,
51
+ generatedAt: now,
52
+ installedWithVersion: pinnedVersion,
53
+ installedAt: now,
54
+ mode,
55
+ org,
56
+ designRepo,
57
+ templateSchemaVersion: constants_1.SCHEMA_VERSION,
58
+ };
59
+ const outPath = node_path_1.default.join(targetDir, '.sdx', 'install.json');
60
+ (0, fs_1.writeJsonFile)(outPath, payload);
61
+ return outPath;
62
+ }
63
+ function writePinnedWrapperScript(targetDir, pinnedVersion) {
64
+ const scriptPath = node_path_1.default.join(targetDir, 'scripts', 'sdx');
65
+ const body = [
66
+ '#!/usr/bin/env bash',
67
+ 'set -euo pipefail',
68
+ `npx --yes sdx-cli@${pinnedVersion} sdx "$@"`,
69
+ '',
70
+ ].join('\n');
71
+ (0, fs_1.writeTextFile)(scriptPath, body);
72
+ node_fs_1.default.chmodSync(scriptPath, 0o755);
73
+ return scriptPath;
74
+ }
75
+ function buildSeededAllServicesMap(org, targetDir) {
76
+ const context = (0, project_1.loadProject)(targetDir);
77
+ const repos = (0, repoRegistry_1.listAllRepos)(context.db);
78
+ (0, project_1.ensureMapDir)('all-services', targetDir);
79
+ const manifest = (0, scope_1.createScopeManifest)('all-services', org, repos.map((repo) => repo.name), targetDir);
80
+ const archivedOrFork = repos.filter((repo) => repo.archived || repo.fork).map((repo) => repo.name);
81
+ manifest.explicitExclude = [...new Set([...manifest.explicitExclude, ...archivedOrFork])].sort((a, b) => a.localeCompare(b));
82
+ (0, scope_1.saveScopeManifest)(manifest, targetDir);
83
+ (0, project_1.recordRun)(context.db, 'map_create', 'ok', 'all-services', { org, discovered: repos.length, seeded: true });
84
+ const repoMap = new Map(repos.map((repo) => [repo.name, repo]));
85
+ const artifact = (0, mapBuilder_1.buildServiceMapArtifact)('all-services', manifest, repoMap);
86
+ const mapDir = node_path_1.default.join(targetDir, 'maps', 'all-services');
87
+ (0, fs_1.writeJsonFile)(node_path_1.default.join(mapDir, 'service-map.json'), artifact);
88
+ (0, fs_1.writeTextFile)(node_path_1.default.join(mapDir, 'service-map.md'), (0, mapBuilder_1.renderServiceMapMarkdown)(artifact));
89
+ (0, fs_1.writeTextFile)(node_path_1.default.join(mapDir, 'service-map.mmd'), (0, mapBuilder_1.renderServiceMapMermaid)(artifact));
90
+ (0, project_1.recordRun)(context.db, 'map_build', 'ok', 'all-services', {
91
+ seeded: true,
92
+ repos: artifact.repos.length,
93
+ nodes: artifact.nodes.length,
94
+ edges: artifact.edges.length,
95
+ });
96
+ context.db.close();
97
+ }
98
+ async function bootstrapConsumer(options) {
99
+ const cwd = options.cwd ?? process.cwd();
100
+ const mode = options.mode;
101
+ const targetDir = resolveConsumerTargetDir(mode, options.designRepo, options.targetDir, cwd);
102
+ const pinnedVersion = resolvePinnedVersion(options.pin, (0, version_1.getCliPackageVersion)());
103
+ const warnings = [];
104
+ const nextSteps = [];
105
+ const result = {
106
+ targetDir,
107
+ pinnedVersion,
108
+ mode,
109
+ remoteCreated: false,
110
+ seededDefaultMap: false,
111
+ warnings,
112
+ nextSteps,
113
+ };
114
+ if (options.createRemote && mode !== 'dedicated') {
115
+ throw new Error('--create-remote can only be used with --mode dedicated.');
116
+ }
117
+ if (mode === 'dedicated' && options.createRemote) {
118
+ const token = process.env.GITHUB_TOKEN;
119
+ if (!token) {
120
+ throw new Error('Missing GitHub token. Set GITHUB_TOKEN to use --create-remote.');
121
+ }
122
+ const remote = await (0, github_1.ensureOrgRepo)(options.org, options.designRepo, token);
123
+ result.remoteCreated = remote.created;
124
+ result.remoteUrl = remote.htmlUrl;
125
+ }
126
+ const initContext = (0, project_1.initProject)(targetDir);
127
+ (0, bootstrap_1.createBootstrapStructure)(options.org, options.designRepo, targetDir);
128
+ initContext.config.outputRepo.org = options.org;
129
+ initContext.config.outputRepo.repo = options.designRepo;
130
+ initContext.config.github.defaultOrg = options.org;
131
+ (0, config_1.saveConfig)(initContext.config, targetDir);
132
+ (0, project_1.recordRun)(initContext.db, 'bootstrap_org', 'ok', undefined, {
133
+ org: options.org,
134
+ repo: options.designRepo,
135
+ source: 'bootstrap_consumer',
136
+ });
137
+ initContext.db.close();
138
+ writeInstallManifest(targetDir, options.org, options.designRepo, mode, pinnedVersion);
139
+ writePinnedWrapperScript(targetDir, pinnedVersion);
140
+ if (mode === 'dedicated' && !options.createRemote) {
141
+ nextSteps.push(`gh repo create ${options.org}/${options.designRepo} --private`);
142
+ nextSteps.push(`cd ${targetDir}`);
143
+ nextSteps.push(`git init`);
144
+ nextSteps.push(`git remote add origin git@github.com:${options.org}/${options.designRepo}.git`);
145
+ }
146
+ if (options.seedDefaultMap) {
147
+ const context = (0, project_1.loadProject)(targetDir);
148
+ const tokenEnvName = context.config.github.tokenEnv;
149
+ context.db.close();
150
+ const token = process.env[tokenEnvName];
151
+ if (!token) {
152
+ warnings.push(`Skipping --seed-default-map: missing ${tokenEnvName}.`);
153
+ }
154
+ else {
155
+ const syncContext = (0, project_1.loadProject)(targetDir);
156
+ const repos = await (0, github_1.fetchOrgRepos)(options.org, token);
157
+ (0, repoRegistry_1.upsertRepos)(syncContext.db, repos);
158
+ (0, project_1.recordRun)(syncContext.db, 'repo_sync', 'ok', undefined, {
159
+ org: options.org,
160
+ count: repos.length,
161
+ seeded: true,
162
+ });
163
+ syncContext.db.close();
164
+ if (repos.length === 0) {
165
+ warnings.push(`Skipping --seed-default-map: no repositories discovered for org '${options.org}'.`);
166
+ }
167
+ else {
168
+ buildSeededAllServicesMap(options.org, targetDir);
169
+ result.seededDefaultMap = true;
170
+ }
171
+ }
172
+ }
173
+ const finalize = (0, project_1.loadProject)(targetDir);
174
+ (0, project_1.recordRun)(finalize.db, 'bootstrap_consumer', 'ok', undefined, {
175
+ org: options.org,
176
+ designRepo: options.designRepo,
177
+ mode,
178
+ targetDir,
179
+ pinnedVersion,
180
+ createRemote: Boolean(options.createRemote),
181
+ seedDefaultMap: Boolean(options.seedDefaultMap),
182
+ seededDefaultMap: result.seededDefaultMap,
183
+ warnings: warnings.length,
184
+ });
185
+ finalize.db.close();
186
+ return result;
187
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseBootstrapQuickTarget = parseBootstrapQuickTarget;
4
+ const DEFAULT_DESIGN_REPO_SUFFIX = 'system-designer';
5
+ const ORG_REPO_PATTERN = /^[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+$/;
6
+ const ORG_PATTERN = /^[a-zA-Z0-9._-]+$/;
7
+ function normalizeTarget(target) {
8
+ let normalized = target.trim();
9
+ normalized = normalized.replace(/^https?:\/\/github\.com\//i, '');
10
+ normalized = normalized.replace(/\.git$/i, '');
11
+ normalized = normalized.replace(/^\/+|\/+$/g, '');
12
+ return normalized;
13
+ }
14
+ function defaultDesignRepoForOrg(org) {
15
+ return `${org}-${DEFAULT_DESIGN_REPO_SUFFIX}`;
16
+ }
17
+ function parseBootstrapQuickTarget(target) {
18
+ const normalized = normalizeTarget(target);
19
+ if (ORG_REPO_PATTERN.test(normalized)) {
20
+ const [org, designRepo] = normalized.split('/');
21
+ return { org, designRepo };
22
+ }
23
+ if (ORG_PATTERN.test(normalized)) {
24
+ return { org: normalized, designRepo: defaultDesignRepoForOrg(normalized) };
25
+ }
26
+ throw new Error("Invalid target. Use <org> or <org>/<design-repo> (example: dana0550/dana0550-system-designer).");
27
+ }
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runCodexTask = runCodexTask;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const node_child_process_1 = require("node:child_process");
9
+ const constants_1 = require("./constants");
10
+ const fs_1 = require("./fs");
11
+ const paths_1 = require("./paths");
12
+ function timestamp() {
13
+ return new Date().toISOString().replace(/[:.]/g, '-');
14
+ }
15
+ function loadServiceMap(mapId, cwd = process.cwd()) {
16
+ const mapPath = node_path_1.default.join((0, paths_1.getMapDir)(mapId, cwd), 'service-map.json');
17
+ return (0, fs_1.readJsonFile)(mapPath);
18
+ }
19
+ function createPrompt(taskType, contextPack, inputBody) {
20
+ const lines = [
21
+ `Task Type: ${taskType}`,
22
+ '',
23
+ 'Use the attached architecture context and produce actionable engineering output.',
24
+ '',
25
+ 'Context (JSON):',
26
+ JSON.stringify(contextPack, null, 2),
27
+ '',
28
+ ];
29
+ if (inputBody) {
30
+ lines.push('Additional Input:');
31
+ lines.push(inputBody);
32
+ lines.push('');
33
+ }
34
+ return `${lines.join('\n')}\n`;
35
+ }
36
+ function runCodexTask(input) {
37
+ const cwd = input.cwd ?? process.cwd();
38
+ const serviceMap = loadServiceMap(input.mapId, cwd);
39
+ const contextPack = {
40
+ schemaVersion: constants_1.SCHEMA_VERSION,
41
+ generatedAt: new Date().toISOString(),
42
+ mapId: input.mapId,
43
+ taskType: input.taskType,
44
+ constraints: input.constraints ?? [
45
+ 'Respect existing service ownership boundaries.',
46
+ 'Keep contract changes backward compatible by default.',
47
+ 'Return explicit assumptions and risks.',
48
+ ],
49
+ affectedRepos: [...serviceMap.repos],
50
+ graphSlice: {
51
+ nodes: serviceMap.nodes,
52
+ edges: serviceMap.edges,
53
+ },
54
+ inputFile: input.inputFile,
55
+ };
56
+ const codexDir = (0, paths_1.getCodexDir)(cwd);
57
+ const stamp = timestamp();
58
+ const contextPackPath = node_path_1.default.join(codexDir, 'context-packs', `${stamp}-${input.taskType}.json`);
59
+ const promptPath = node_path_1.default.join(codexDir, 'runs', `${stamp}-${input.taskType}.prompt.txt`);
60
+ const runMarkdownPath = node_path_1.default.join(codexDir, 'runs', `${stamp}-${input.taskType}.md`);
61
+ const runJsonPath = node_path_1.default.join(codexDir, 'runs', `${stamp}-${input.taskType}.json`);
62
+ (0, fs_1.writeJsonFile)(contextPackPath, contextPack);
63
+ const inputBody = input.inputFile ? (0, fs_1.safeReadText)(node_path_1.default.resolve(input.inputFile)) : undefined;
64
+ const prompt = createPrompt(input.taskType, contextPack, inputBody);
65
+ (0, fs_1.writeTextFile)(promptPath, prompt);
66
+ let invocationMode = 'stdin';
67
+ let result = (0, node_child_process_1.spawnSync)(input.codexCommand, ['exec'], {
68
+ input: prompt,
69
+ encoding: 'utf8',
70
+ cwd,
71
+ timeout: 1000 * 60 * 10,
72
+ });
73
+ const firstCombined = `${result.stdout ?? ''}\n${result.stderr ?? ''}`.toLowerCase();
74
+ const needsArgFallback = result.status !== 0 &&
75
+ /usage|missing|required/.test(firstCombined) &&
76
+ !/error/.test(firstCombined);
77
+ if (needsArgFallback) {
78
+ invocationMode = 'argv';
79
+ result = (0, node_child_process_1.spawnSync)(input.codexCommand, ['exec', prompt], {
80
+ encoding: 'utf8',
81
+ cwd,
82
+ timeout: 1000 * 60 * 10,
83
+ });
84
+ }
85
+ const stdout = result.stdout ?? '';
86
+ const stderr = result.stderr ?? '';
87
+ const exitCode = typeof result.status === 'number' ? result.status : 1;
88
+ (0, fs_1.writeJsonFile)(runJsonPath, {
89
+ schemaVersion: constants_1.SCHEMA_VERSION,
90
+ generatedAt: new Date().toISOString(),
91
+ mapId: input.mapId,
92
+ taskType: input.taskType,
93
+ command: input.codexCommand,
94
+ args: invocationMode === 'stdin' ? ['exec', '<stdin:prompt>'] : ['exec', '<prompt-arg>'],
95
+ invocationMode,
96
+ exitCode,
97
+ stdout,
98
+ stderr,
99
+ contextPackPath,
100
+ promptPath,
101
+ });
102
+ const markdown = [
103
+ `# Codex Run: ${input.taskType}`,
104
+ '',
105
+ `- Generated: ${new Date().toISOString()}`,
106
+ `- Map: ${input.mapId}`,
107
+ `- Command: ${input.codexCommand}`,
108
+ `- Exit Code: ${exitCode}`,
109
+ `- Invocation Mode: ${invocationMode}`,
110
+ `- Context Pack: ${contextPackPath}`,
111
+ `- Prompt File: ${promptPath}`,
112
+ '',
113
+ '## Stdout',
114
+ '',
115
+ '```txt',
116
+ stdout,
117
+ '```',
118
+ '',
119
+ '## Stderr',
120
+ '',
121
+ '```txt',
122
+ stderr,
123
+ '```',
124
+ '',
125
+ ].join('\n');
126
+ (0, fs_1.writeTextFile)(runMarkdownPath, markdown);
127
+ if (result.error) {
128
+ throw new Error(`Codex invocation failed: ${result.error.message}`);
129
+ }
130
+ return {
131
+ contextPackPath,
132
+ promptPath,
133
+ runMarkdownPath,
134
+ runJsonPath,
135
+ exitCode,
136
+ invocationMode,
137
+ };
138
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDefaultConfig = createDefaultConfig;
4
+ exports.saveConfig = saveConfig;
5
+ exports.loadConfig = loadConfig;
6
+ exports.hasConfig = hasConfig;
7
+ const constants_1 = require("./constants");
8
+ const fs_1 = require("./fs");
9
+ const paths_1 = require("./paths");
10
+ function createDefaultConfig(cwd = process.cwd()) {
11
+ const now = new Date().toISOString();
12
+ return {
13
+ schemaVersion: constants_1.SCHEMA_VERSION,
14
+ createdAt: now,
15
+ updatedAt: now,
16
+ outputRepo: {
17
+ rootDir: (0, paths_1.getProjectRoot)(cwd),
18
+ },
19
+ codex: {
20
+ cmd: process.env.CODEX_CMD ?? 'codex',
21
+ },
22
+ github: {
23
+ tokenEnv: 'GITHUB_TOKEN',
24
+ },
25
+ };
26
+ }
27
+ function saveConfig(config, cwd = process.cwd()) {
28
+ config.updatedAt = new Date().toISOString();
29
+ (0, fs_1.writeJsonFile)((0, paths_1.getConfigPath)(cwd), config);
30
+ }
31
+ function loadConfig(cwd = process.cwd()) {
32
+ const configPath = (0, paths_1.getConfigPath)(cwd);
33
+ if (!(0, fs_1.fileExists)(configPath)) {
34
+ throw new Error('sdx is not initialized. Run `sdx init` first.');
35
+ }
36
+ return (0, fs_1.readJsonFile)(configPath);
37
+ }
38
+ function hasConfig(cwd = process.cwd()) {
39
+ return (0, fs_1.fileExists)((0, paths_1.getConfigPath)(cwd));
40
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PRIMER_DIMENSIONS = exports.REQUIRED_NFR_KEYWORDS = exports.SCHEMA_VERSION = exports.DB_FILE = exports.CONFIG_FILE = exports.APP_DIR = void 0;
4
+ exports.APP_DIR = '.sdx';
5
+ exports.CONFIG_FILE = 'config.json';
6
+ exports.DB_FILE = 'state.db';
7
+ exports.SCHEMA_VERSION = '1.0.0';
8
+ exports.REQUIRED_NFR_KEYWORDS = [
9
+ 'latency',
10
+ 'availability',
11
+ 'durability',
12
+ 'slo',
13
+ 'failure',
14
+ ];
15
+ exports.PRIMER_DIMENSIONS = [
16
+ 'scalability',
17
+ 'reliability',
18
+ 'consistency_model',
19
+ 'data_design',
20
+ 'api_style',
21
+ 'caching',
22
+ 'async_patterns',
23
+ 'security',
24
+ 'observability',
25
+ 'operational_tradeoffs',
26
+ ];