@clawplays/ospec-cli 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -488
- package/README.zh-CN.md +169 -485
- package/dist/cli.js +104 -8
- package/dist/commands/DocsCommand.js +3 -2
- package/dist/commands/InitCommand.js +69 -11
- package/dist/commands/SkillCommand.js +41 -19
- package/dist/commands/StatusCommand.js +27 -8
- package/dist/commands/UpdateCommand.js +19 -5
- package/dist/services/ProjectService.js +173 -359
- package/dist/services/templates/ProjectTemplateBuilder.js +4 -10
- package/dist/services/templates/TemplateInputFactory.js +14 -7
- package/dist/utils/subcommandHelp.js +5 -5
- package/package.json +9 -2
- package/scripts/postinstall.js +13 -7
package/dist/cli.js
CHANGED
|
@@ -224,7 +224,98 @@ const services_1 = require("./services");
|
|
|
224
224
|
|
|
225
225
|
|
|
226
226
|
|
|
227
|
-
const CLI_VERSION = '0.
|
|
227
|
+
const CLI_VERSION = '0.2.0';
|
|
228
|
+
|
|
229
|
+
function showInitUsage() {
|
|
230
|
+
console.log('Usage: ospec init [root-dir] [--summary "..."] [--tech-stack node,react] [--architecture "..."] [--document-language zh-CN|en-US]');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function parseInitCommandArgs(commandArgs) {
|
|
234
|
+
let rootDir;
|
|
235
|
+
const options = {};
|
|
236
|
+
for (let index = 0; index < commandArgs.length; index += 1) {
|
|
237
|
+
const arg = commandArgs[index];
|
|
238
|
+
if (arg === '--help' || arg === '-h' || arg === 'help') {
|
|
239
|
+
showInitUsage();
|
|
240
|
+
process.exit(0);
|
|
241
|
+
}
|
|
242
|
+
if (arg === '--summary') {
|
|
243
|
+
const value = commandArgs[index + 1];
|
|
244
|
+
if (!value || value.startsWith('--')) {
|
|
245
|
+
console.error('Error: --summary requires a value');
|
|
246
|
+
showInitUsage();
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
options.summary = value.trim();
|
|
250
|
+
index += 1;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (arg.startsWith('--summary=')) {
|
|
254
|
+
options.summary = arg.slice('--summary='.length).trim();
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (arg === '--tech-stack') {
|
|
258
|
+
const value = commandArgs[index + 1];
|
|
259
|
+
if (!value || value.startsWith('--')) {
|
|
260
|
+
console.error('Error: --tech-stack requires a comma-separated value');
|
|
261
|
+
showInitUsage();
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
options.techStack = value.split(',').map(item => item.trim()).filter(Boolean);
|
|
265
|
+
index += 1;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (arg.startsWith('--tech-stack=')) {
|
|
269
|
+
options.techStack = arg.slice('--tech-stack='.length).split(',').map(item => item.trim()).filter(Boolean);
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
if (arg === '--architecture') {
|
|
273
|
+
const value = commandArgs[index + 1];
|
|
274
|
+
if (!value || value.startsWith('--')) {
|
|
275
|
+
console.error('Error: --architecture requires a value');
|
|
276
|
+
showInitUsage();
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
options.architecture = value.trim();
|
|
280
|
+
index += 1;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (arg.startsWith('--architecture=')) {
|
|
284
|
+
options.architecture = arg.slice('--architecture='.length).trim();
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (arg === '--document-language' || arg === '--lang') {
|
|
288
|
+
const value = commandArgs[index + 1];
|
|
289
|
+
if (!value || value.startsWith('--')) {
|
|
290
|
+
console.error('Error: --document-language requires a value');
|
|
291
|
+
showInitUsage();
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
options.documentLanguage = value.trim();
|
|
295
|
+
index += 1;
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (arg.startsWith('--document-language=')) {
|
|
299
|
+
options.documentLanguage = arg.slice('--document-language='.length).trim();
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (arg.startsWith('--lang=')) {
|
|
303
|
+
options.documentLanguage = arg.slice('--lang='.length).trim();
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (!rootDir) {
|
|
307
|
+
rootDir = arg;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
console.error(`Error: unexpected argument "${arg}"`);
|
|
311
|
+
showInitUsage();
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
rootDir,
|
|
316
|
+
options,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
228
319
|
|
|
229
320
|
|
|
230
321
|
|
|
@@ -432,7 +523,9 @@ async function main() {
|
|
|
432
523
|
|
|
433
524
|
|
|
434
525
|
|
|
435
|
-
|
|
526
|
+
const { rootDir, options } = parseInitCommandArgs(commandArgs);
|
|
527
|
+
|
|
528
|
+
await initCmd.execute(rootDir, options);
|
|
436
529
|
|
|
437
530
|
|
|
438
531
|
|
|
@@ -927,7 +1020,7 @@ OSpec CLI v${CLI_VERSION}
|
|
|
927
1020
|
Usage: ospec <command> [options]
|
|
928
1021
|
|
|
929
1022
|
Commands:
|
|
930
|
-
init [root-dir] Initialize
|
|
1023
|
+
init [root-dir] Initialize OSpec to a change-ready state
|
|
931
1024
|
new <change-name> [root] Create a new change (supports --flags)
|
|
932
1025
|
verify [path] Verify change completion
|
|
933
1026
|
progress [path] Show workflow progress
|
|
@@ -941,7 +1034,7 @@ Commands:
|
|
|
941
1034
|
docs [action] [path] Docs helpers (status, generate)
|
|
942
1035
|
skills [action] [path] Skills status helpers (status)
|
|
943
1036
|
plugins [action] [path] Plugin helpers (list, status, enable, disable, approve, reject)
|
|
944
|
-
skill [action] [skill] [dir] Skill package helpers (
|
|
1037
|
+
skill [action] [skill] [dir] Skill package helpers (managed skills: ospec, ospec-change)
|
|
945
1038
|
index [action] [path] Index helpers (check, build)
|
|
946
1039
|
workflow [action] Workflow configuration (show, list-flags)
|
|
947
1040
|
update [path] Refresh protocol docs, tooling, hooks, and installed skills; does not enable or migrate plugins
|
|
@@ -950,6 +1043,7 @@ Commands:
|
|
|
950
1043
|
|
|
951
1044
|
Examples:
|
|
952
1045
|
ospec init
|
|
1046
|
+
ospec init . --summary "Internal admin portal" --tech-stack node,react,postgres
|
|
953
1047
|
ospec new onboarding-flow
|
|
954
1048
|
ospec new landing-refresh . --flags ui_change,page_design
|
|
955
1049
|
ospec verify ./changes/active/onboarding-flow
|
|
@@ -972,11 +1066,13 @@ Examples:
|
|
|
972
1066
|
ospec plugins enable checkpoint . --base-url http://127.0.0.1:3000
|
|
973
1067
|
ospec plugins run checkpoint ./changes/active/onboarding-flow
|
|
974
1068
|
ospec plugins approve stitch ./changes/active/onboarding-flow
|
|
975
|
-
ospec skill status
|
|
976
|
-
ospec skill install
|
|
1069
|
+
ospec skill status ospec
|
|
1070
|
+
ospec skill install ospec
|
|
1071
|
+
ospec skill status ospec-change
|
|
1072
|
+
ospec skill install ospec-change
|
|
977
1073
|
ospec skill install ospec-init
|
|
978
|
-
ospec skill status-claude
|
|
979
|
-
ospec skill install-claude
|
|
1074
|
+
ospec skill status-claude ospec
|
|
1075
|
+
ospec skill install-claude ospec
|
|
980
1076
|
ospec index build
|
|
981
1077
|
ospec batch stats
|
|
982
1078
|
ospec changes status
|
|
@@ -19,8 +19,8 @@ class DocsCommand extends BaseCommand_1.BaseCommand {
|
|
|
19
19
|
throw new Error('Project is not initialized. Run "ospec init" first.');
|
|
20
20
|
}
|
|
21
21
|
const result = await services_1.services.projectService.generateProjectKnowledge(targetPath);
|
|
22
|
-
console.log('\nProject Knowledge
|
|
23
|
-
console.log('
|
|
22
|
+
console.log('\nProject Knowledge Synced');
|
|
23
|
+
console.log('======================\n');
|
|
24
24
|
console.log(`Project: ${result.projectName}`);
|
|
25
25
|
console.log(`Mode: ${result.mode}`);
|
|
26
26
|
console.log(`Created files: ${result.createdFiles.length}`);
|
|
@@ -29,6 +29,7 @@ class DocsCommand extends BaseCommand_1.BaseCommand {
|
|
|
29
29
|
console.log(`Direct-copy assets created: ${result.directCopyCreatedFiles.length}`);
|
|
30
30
|
console.log(`Hooks installed: ${result.hookInstalledFiles.length}`);
|
|
31
31
|
console.log(`Runtime-generated files: ${result.runtimeGeneratedFiles.join(', ') || '-'}`);
|
|
32
|
+
console.log('Purpose: refresh, repair, or backfill project knowledge docs after initialization');
|
|
32
33
|
console.log('Business scaffold: not applied by docs generate');
|
|
33
34
|
console.log('Bootstrap summary: not generated by docs generate');
|
|
34
35
|
if (result.firstChangeSuggestion) {
|
|
@@ -34,32 +34,90 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.InitCommand = void 0;
|
|
37
|
+
const os_1 = require("os");
|
|
37
38
|
const path = __importStar(require("path"));
|
|
38
39
|
const services_1 = require("../services");
|
|
39
40
|
const BaseCommand_1 = require("./BaseCommand");
|
|
41
|
+
const SkillCommand_1 = require("./SkillCommand");
|
|
40
42
|
class InitCommand extends BaseCommand_1.BaseCommand {
|
|
41
|
-
async execute(rootDir) {
|
|
43
|
+
async execute(rootDir, input = {}) {
|
|
42
44
|
try {
|
|
43
45
|
const targetDir = rootDir || process.cwd();
|
|
44
46
|
this.logger.info(`Initializing OSpec project at ${targetDir}`);
|
|
45
47
|
const structure = await services_1.services.projectService.detectProjectStructure(targetDir);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
let protocolShellCreated = false;
|
|
49
|
+
if (!structure.initialized) {
|
|
50
|
+
const config = await services_1.services.configManager.createDefaultConfig('full');
|
|
51
|
+
await services_1.services.projectService.initializeProtocolShellProject(targetDir, config.mode, {
|
|
52
|
+
projectName: path.basename(targetDir),
|
|
53
|
+
...input,
|
|
54
|
+
});
|
|
55
|
+
protocolShellCreated = true;
|
|
49
56
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
this.
|
|
56
|
-
this.info(
|
|
57
|
+
else {
|
|
58
|
+
this.info(' Protocol shell already present; reconciling the repository to a change-ready state');
|
|
59
|
+
}
|
|
60
|
+
const result = await services_1.services.projectService.generateProjectKnowledge(targetDir, input);
|
|
61
|
+
const skillResult = await this.syncInstalledSkills();
|
|
62
|
+
this.success(`Project initialized to change-ready state at ${targetDir}`);
|
|
63
|
+
this.info(` Protocol shell: ${protocolShellCreated ? 'created' : 'already present'}`);
|
|
64
|
+
this.info(` Project knowledge: ${result.createdFiles.length} created, ${result.refreshedFiles.length} refreshed, ${result.skippedFiles.length} skipped`);
|
|
65
|
+
this.info(` Document language: ${result.documentLanguage}`);
|
|
66
|
+
this.info(` Codex skills: ${this.formatManagedSkills(skillResult.codex)}`);
|
|
67
|
+
if (skillResult.claude.length > 0) {
|
|
68
|
+
this.info(` Claude skills: ${this.formatManagedSkills(skillResult.claude)}`);
|
|
69
|
+
}
|
|
70
|
+
if (input.summary || (Array.isArray(input.techStack) && input.techStack.length > 0) || input.architecture) {
|
|
71
|
+
this.info(' Applied user-provided project context to the generated knowledge docs');
|
|
72
|
+
}
|
|
73
|
+
if (result.firstChangeSuggestion) {
|
|
74
|
+
this.info(` Suggested first change: ${result.firstChangeSuggestion.name}`);
|
|
75
|
+
}
|
|
76
|
+
this.info(` Next: ospec new <change-name> ${targetDir}`);
|
|
57
77
|
}
|
|
58
78
|
catch (error) {
|
|
59
79
|
this.error(`Failed to initialize project: ${error}`);
|
|
60
80
|
throw error;
|
|
61
81
|
}
|
|
62
82
|
}
|
|
83
|
+
getManagedSkillNames() {
|
|
84
|
+
return ['ospec', 'ospec-change'];
|
|
85
|
+
}
|
|
86
|
+
formatManagedSkills(results) {
|
|
87
|
+
return results.map(result => result.skillName).join(', ');
|
|
88
|
+
}
|
|
89
|
+
async syncInstalledSkills() {
|
|
90
|
+
const skillCommand = new SkillCommand_1.SkillCommand();
|
|
91
|
+
const codex = [];
|
|
92
|
+
for (const skillName of this.getManagedSkillNames()) {
|
|
93
|
+
codex.push(await skillCommand.installSkill('codex', skillName));
|
|
94
|
+
}
|
|
95
|
+
const claude = [];
|
|
96
|
+
if (await this.shouldSyncClaudeSkills()) {
|
|
97
|
+
for (const skillName of this.getManagedSkillNames()) {
|
|
98
|
+
claude.push(await skillCommand.installSkill('claude', skillName));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { codex, claude };
|
|
102
|
+
}
|
|
103
|
+
resolveProviderHome(provider) {
|
|
104
|
+
const envHome = provider === 'claude'
|
|
105
|
+
? String(process.env.CLAUDE_HOME || '').trim()
|
|
106
|
+
: String(process.env.CODEX_HOME || '').trim();
|
|
107
|
+
if (envHome) {
|
|
108
|
+
return path.resolve(envHome);
|
|
109
|
+
}
|
|
110
|
+
return provider === 'claude'
|
|
111
|
+
? path.join((0, os_1.homedir)(), '.claude')
|
|
112
|
+
: path.join((0, os_1.homedir)(), '.codex');
|
|
113
|
+
}
|
|
114
|
+
async shouldSyncClaudeSkills() {
|
|
115
|
+
const claudeHome = this.resolveProviderHome('claude');
|
|
116
|
+
if (String(process.env.CLAUDE_HOME || '').trim()) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return services_1.services.fileService.exists(claudeHome);
|
|
120
|
+
}
|
|
63
121
|
}
|
|
64
122
|
exports.InitCommand = InitCommand;
|
|
65
123
|
//# sourceMappingURL=InitCommand.js.map
|
|
@@ -28,11 +28,11 @@ const ACTION_SKILLS = [
|
|
|
28
28
|
|
|
29
29
|
title: 'OSpec Init',
|
|
30
30
|
|
|
31
|
-
description: 'Initialize
|
|
31
|
+
description: 'Initialize an OSpec repository to change-ready state without creating the first change automatically.',
|
|
32
32
|
|
|
33
|
-
shortDescription: 'Initialize OSpec
|
|
33
|
+
shortDescription: 'Initialize OSpec to change-ready',
|
|
34
34
|
|
|
35
|
-
defaultPrompt: 'Use $ospec-init to
|
|
35
|
+
defaultPrompt: 'Use $ospec-init to initialize the target directory with ospec init so the repository ends in change-ready state. Reuse existing project docs when available. If the repository lacks a usable project overview and you are in an AI-assisted flow, ask one concise question for project summary or tech stack before calling ospec init with those inputs; if the user declines, run plain ospec init and allow placeholder docs. Verify the protocol-shell files and project knowledge docs on disk. Do not create the first change automatically.',
|
|
36
36
|
|
|
37
37
|
markdown: `# OSpec Init
|
|
38
38
|
|
|
@@ -46,15 +46,17 @@ Use this action when the user intent is initialization.
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
-
|
|
49
|
+
- use \`ospec init [path]\` so the repository ends in change-ready state
|
|
50
50
|
|
|
51
|
-
-
|
|
51
|
+
- verify \`.skillrc\`, \`.ospec/\`, \`changes/\`, \`SKILL.md\`, \`SKILL.index.json\`, \`build-index-auto.cjs\`, \`for-ai/\`, and \`docs/project/\` files on disk
|
|
52
52
|
|
|
53
|
-
-
|
|
53
|
+
- if project overview context is missing and AI can ask follow-up questions, ask for a brief summary or tech stack before initialization; if the user declines, fall back to placeholder docs
|
|
54
|
+
|
|
55
|
+
- use \`ospec status [path]\` only when you want an explicit summary or troubleshooting snapshot
|
|
54
56
|
|
|
55
57
|
- do not assume a web template when the project type is unclear
|
|
56
58
|
|
|
57
|
-
- do not
|
|
59
|
+
- do not create the first change unless explicitly requested
|
|
58
60
|
|
|
59
61
|
|
|
60
62
|
|
|
@@ -64,10 +66,12 @@ Use this action when the user intent is initialization.
|
|
|
64
66
|
|
|
65
67
|
\`\`\`bash
|
|
66
68
|
|
|
67
|
-
ospec status [path]
|
|
68
|
-
|
|
69
69
|
ospec init [path]
|
|
70
70
|
|
|
71
|
+
ospec init [path] --summary "..." --tech-stack node,react
|
|
72
|
+
|
|
73
|
+
ospec status [path]
|
|
74
|
+
|
|
71
75
|
\`\`\`
|
|
72
76
|
|
|
73
77
|
`,
|
|
@@ -130,17 +134,17 @@ ospec changes status [path]
|
|
|
130
134
|
|
|
131
135
|
title: 'OSpec Backfill',
|
|
132
136
|
|
|
133
|
-
description: '
|
|
137
|
+
description: 'Refresh or repair the project knowledge layer for an initialized repository without creating a change.',
|
|
134
138
|
|
|
135
|
-
shortDescription: '
|
|
139
|
+
shortDescription: 'Refresh project knowledge layer',
|
|
136
140
|
|
|
137
|
-
defaultPrompt: 'Use $ospec-backfill to backfill the project knowledge layer
|
|
141
|
+
defaultPrompt: 'Use $ospec-backfill to refresh, repair, or backfill the project knowledge layer for an initialized repository. Prefer ospec docs generate when you only need docs maintenance, keep scaffold explicit, and do not create the first change automatically.',
|
|
138
142
|
|
|
139
143
|
markdown: `# OSpec Backfill
|
|
140
144
|
|
|
141
145
|
|
|
142
146
|
|
|
143
|
-
Use this action after the
|
|
147
|
+
Use this action after the repository is already initialized and the project knowledge docs need maintenance.
|
|
144
148
|
|
|
145
149
|
|
|
146
150
|
|
|
@@ -148,7 +152,7 @@ Use this action after the protocol shell already exists and the repository still
|
|
|
148
152
|
|
|
149
153
|
|
|
150
154
|
|
|
151
|
-
- require
|
|
155
|
+
- require an initialized repository first
|
|
152
156
|
|
|
153
157
|
- prefer \`ospec docs generate [path]\`
|
|
154
158
|
|
|
@@ -787,7 +791,7 @@ class SkillCommand extends BaseCommand_1.BaseCommand {
|
|
|
787
791
|
|
|
788
792
|
title: 'OSpec',
|
|
789
793
|
|
|
790
|
-
description: 'Protocol-shell-first OSpec workflow for initialization,
|
|
794
|
+
description: 'Protocol-shell-first OSpec workflow for inspection, change-ready initialization, docs maintenance, change execution, verification, and archive readiness.',
|
|
791
795
|
|
|
792
796
|
shortDescription: 'Inspect, initialize, and operate OSpec projects',
|
|
793
797
|
|
|
@@ -1025,7 +1029,7 @@ ${markdownBody.trimStart()}`;
|
|
|
1025
1029
|
|
|
1026
1030
|
if (/^---\r?\n/.test(definition.markdown)) {
|
|
1027
1031
|
|
|
1028
|
-
return definition.markdown;
|
|
1032
|
+
return this.ensureFrontmatterDescription(definition.markdown, definition.description);
|
|
1029
1033
|
|
|
1030
1034
|
}
|
|
1031
1035
|
|
|
@@ -1045,6 +1049,24 @@ ${definition.markdown.trimStart()}`;
|
|
|
1045
1049
|
|
|
1046
1050
|
}
|
|
1047
1051
|
|
|
1052
|
+
ensureFrontmatterDescription(markdown, description) {
|
|
1053
|
+
|
|
1054
|
+
if (!/^---\r?\n/.test(markdown)) {
|
|
1055
|
+
|
|
1056
|
+
return markdown;
|
|
1057
|
+
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
if (/^---\r?\n[\s\S]*?\r?\ndescription:\s+/m.test(markdown)) {
|
|
1061
|
+
|
|
1062
|
+
return markdown;
|
|
1063
|
+
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
return markdown.replace(/^---\r?\n/, `---\ndescription: ${description}\n`);
|
|
1067
|
+
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1048
1070
|
escapeYaml(value) {
|
|
1049
1071
|
|
|
1050
1072
|
return value.replace(/"/g, '\\"');
|
|
@@ -1091,7 +1113,7 @@ Prefer this prompt style for new work:
|
|
|
1091
1113
|
|
|
1092
1114
|
2. \`Use ospec to inspect this repository\`
|
|
1093
1115
|
|
|
1094
|
-
3. \`Use ospec to
|
|
1116
|
+
3. \`Use ospec to refresh or repair the project knowledge layer\`
|
|
1095
1117
|
|
|
1096
1118
|
4. \`Use ospec to create and advance a change for this requirement\`
|
|
1097
1119
|
|
|
@@ -1157,7 +1179,7 @@ interface:
|
|
|
1157
1179
|
|
|
1158
1180
|
short_description: "Legacy alias for the OSpec skill"
|
|
1159
1181
|
|
|
1160
|
-
default_prompt: "Use $ospec to
|
|
1182
|
+
default_prompt: "Use $ospec to initialize this directory according to OSpec rules: init should end in change-ready state, reuse existing docs when available, ask for missing summary or tech stack in AI-assisted flows before falling back to placeholder docs, avoid assumed web templates when the project type is unclear, and do not create the first change automatically."
|
|
1161
1183
|
|
|
1162
1184
|
`,
|
|
1163
1185
|
|
|
@@ -1167,7 +1189,7 @@ interface:
|
|
|
1167
1189
|
|
|
1168
1190
|
short_description: "Legacy alias for the OSpec skill"
|
|
1169
1191
|
|
|
1170
|
-
default_prompt: "Use $ospec to
|
|
1192
|
+
default_prompt: "Use $ospec to initialize this directory according to OSpec rules: init should end in change-ready state, reuse existing docs when available, ask for missing summary or tech stack in AI-assisted flows before falling back to placeholder docs, avoid assumed web templates when the project type is unclear, and do not create the first change automatically."
|
|
1171
1193
|
|
|
1172
1194
|
`,
|
|
1173
1195
|
|
|
@@ -8,22 +8,40 @@ class StatusCommand extends BaseCommand_1.BaseCommand {
|
|
|
8
8
|
try {
|
|
9
9
|
const targetPath = projectPath || process.cwd();
|
|
10
10
|
this.logger.info(`Getting status for ${targetPath}`);
|
|
11
|
-
const
|
|
12
|
-
|
|
11
|
+
const structure = await services_1.services.projectService.detectProjectStructure(targetPath);
|
|
12
|
+
const [summary, docs, skills, queuedChanges] = await Promise.all([
|
|
13
13
|
services_1.services.projectService.getProjectSummary(targetPath),
|
|
14
14
|
services_1.services.projectService.getDocsStatus(targetPath),
|
|
15
15
|
services_1.services.projectService.getSkillsStatus(targetPath),
|
|
16
|
-
services_1.services.projectService.getExecutionStatus(targetPath),
|
|
17
|
-
services_1.services.projectService.getActiveChangeStatusReport(targetPath),
|
|
18
16
|
services_1.services.queueService.getQueuedChanges(targetPath),
|
|
19
|
-
services_1.services.runService.getStatusReport(targetPath),
|
|
20
17
|
]);
|
|
18
|
+
let execution = {
|
|
19
|
+
totalActiveChanges: summary.activeChangeCount,
|
|
20
|
+
byStatus: {},
|
|
21
|
+
activeChanges: [],
|
|
22
|
+
};
|
|
23
|
+
let changes = {
|
|
24
|
+
totals: { pass: 0, warn: 0, fail: 0 },
|
|
25
|
+
};
|
|
26
|
+
let runReport = {
|
|
27
|
+
currentRun: undefined,
|
|
28
|
+
stage: undefined,
|
|
29
|
+
nextInstruction: undefined,
|
|
30
|
+
};
|
|
31
|
+
if (structure.initialized) {
|
|
32
|
+
[execution, changes, runReport] = await Promise.all([
|
|
33
|
+
services_1.services.projectService.getExecutionStatus(targetPath),
|
|
34
|
+
services_1.services.projectService.getActiveChangeStatusReport(targetPath),
|
|
35
|
+
services_1.services.runService.getStatusReport(targetPath),
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
21
38
|
console.log('\nProject Status');
|
|
22
39
|
console.log('==============\n');
|
|
23
40
|
console.log(`Name: ${summary.name}`);
|
|
24
41
|
console.log(`Path: ${summary.path}`);
|
|
25
42
|
console.log(`Mode: ${summary.mode ?? 'uninitialized'}`);
|
|
26
43
|
console.log(`Initialized: ${summary.initialized ? 'yes' : 'no'}`);
|
|
44
|
+
console.log(`Change Ready: ${summary.initialized && docs.missingRequired.length === 0 ? 'yes' : 'no'}`);
|
|
27
45
|
console.log(`Structure Level: ${summary.structureLevel}`);
|
|
28
46
|
console.log(`Active Changes: ${summary.activeChangeCount}`);
|
|
29
47
|
console.log(`Queued Changes: ${queuedChanges.length}`);
|
|
@@ -104,13 +122,14 @@ class StatusCommand extends BaseCommand_1.BaseCommand {
|
|
|
104
122
|
getRecommendedNextSteps(projectPath, structure, docs, execution, queuedChanges, runReport) {
|
|
105
123
|
if (!structure.initialized) {
|
|
106
124
|
return [
|
|
107
|
-
`Run "ospec init ${projectPath}" to initialize the
|
|
125
|
+
`Run "ospec init ${projectPath}" to initialize the repository to a change-ready state.`,
|
|
108
126
|
];
|
|
109
127
|
}
|
|
110
128
|
if (docs.missingRequired.length > 0 || docs.coverage < 100) {
|
|
111
129
|
return [
|
|
112
|
-
'The
|
|
113
|
-
`Run "ospec
|
|
130
|
+
'The repository is initialized, but the project knowledge layer is still incomplete.',
|
|
131
|
+
`Run "ospec init ${projectPath}" to reconcile the repository back to change-ready state and regenerate missing project knowledge docs.`,
|
|
132
|
+
`If you only want to refresh or repair docs without rerunning full init messaging, use "ospec docs generate ${projectPath}".`,
|
|
114
133
|
];
|
|
115
134
|
}
|
|
116
135
|
if (execution.totalActiveChanges === 0 && queuedChanges.length === 0) {
|
|
@@ -30,9 +30,9 @@ class UpdateCommand extends BaseCommand_1.BaseCommand {
|
|
|
30
30
|
if (toolingResult.hookInstalledFiles.length > 0) {
|
|
31
31
|
this.info(` git hooks refreshed: ${toolingResult.hookInstalledFiles.join(', ')}`);
|
|
32
32
|
}
|
|
33
|
-
this.info(` codex
|
|
34
|
-
if (skillResult.claude) {
|
|
35
|
-
this.info(` claude
|
|
33
|
+
this.info(` codex skills: ${this.formatManagedSkills(skillResult.codex)}`);
|
|
34
|
+
if (skillResult.claude.length > 0) {
|
|
35
|
+
this.info(` claude skills: ${this.formatManagedSkills(skillResult.claude)}`);
|
|
36
36
|
}
|
|
37
37
|
if (pluginResult.enabledPlugins.length > 0) {
|
|
38
38
|
this.info(` plugin assets refreshed: ${pluginResult.enabledPlugins.join(', ')}`);
|
|
@@ -222,10 +222,24 @@ class UpdateCommand extends BaseCommand_1.BaseCommand {
|
|
|
222
222
|
}
|
|
223
223
|
return { createdFiles, skippedFiles };
|
|
224
224
|
}
|
|
225
|
+
getManagedSkillNames() {
|
|
226
|
+
return ['ospec', 'ospec-change'];
|
|
227
|
+
}
|
|
228
|
+
formatManagedSkills(results) {
|
|
229
|
+
return results.map(result => result.skillName).join(', ');
|
|
230
|
+
}
|
|
225
231
|
async syncInstalledSkills() {
|
|
226
232
|
const skillCommand = new SkillCommand_1.SkillCommand();
|
|
227
|
-
const codex =
|
|
228
|
-
const
|
|
233
|
+
const codex = [];
|
|
234
|
+
for (const skillName of this.getManagedSkillNames()) {
|
|
235
|
+
codex.push(await skillCommand.installSkill('codex', skillName));
|
|
236
|
+
}
|
|
237
|
+
const claude = [];
|
|
238
|
+
if (await this.shouldSyncClaudeSkills()) {
|
|
239
|
+
for (const skillName of this.getManagedSkillNames()) {
|
|
240
|
+
claude.push(await skillCommand.installSkill('claude', skillName));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
229
243
|
return { codex, claude };
|
|
230
244
|
}
|
|
231
245
|
resolveProviderHome(provider) {
|