@nan0web/release 1.0.1 → 1.0.3

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 (109) hide show
  1. package/README.md +24 -42
  2. package/bin/nan0release.js +27 -42
  3. package/package.json +48 -42
  4. package/project.yaml +79 -0
  5. package/src/README.md.js +90 -93
  6. package/src/Release/Document.js +13 -19
  7. package/src/Release/MarkdownToTest.js +35 -33
  8. package/src/Release/Person.js +3 -8
  9. package/src/Release/Scanner.js +179 -0
  10. package/src/Release.js +19 -12
  11. package/src/Team.js +9 -9
  12. package/src/architecture/ProjectManagementAsCode.js +8 -8
  13. package/src/cli/index.js +9 -0
  14. package/src/co/AppCommandOptions.js +4 -6
  15. package/src/commands/CloseCommand.js +67 -0
  16. package/src/commands/Command.js +48 -0
  17. package/src/commands/DepsCommand.js +143 -0
  18. package/src/commands/InitCommand.js +48 -56
  19. package/src/commands/PublishCommand.js +80 -119
  20. package/src/commands/SpecCommand.js +41 -0
  21. package/src/commands/StatusCommand.js +26 -0
  22. package/src/db/ReleaseDB.js +5 -5
  23. package/src/index.js +18 -0
  24. package/src/ui/cli/ReleaseCLi.js +52 -0
  25. package/src/utils/runSpawn.js +0 -0
  26. package/types/Release/Document.d.ts +2 -2
  27. package/types/Release/Person.d.ts +3 -3
  28. package/types/Release/Scanner.d.ts +90 -0
  29. package/types/Release.d.ts +37 -33
  30. package/types/ReleaseCLI.d.ts +29 -28
  31. package/types/Team.d.ts +3 -3
  32. package/types/architecture/ProjectManagementAsCode.d.ts +1 -1
  33. package/types/cli/index.d.ts +2 -0
  34. package/types/co/AppCommandMessage.d.ts +10 -11
  35. package/types/co/AppCommandOptions.d.ts +1 -2
  36. package/types/commands/BaseCommand.d.ts +23 -0
  37. package/types/commands/ChatCommand.d.ts +9 -9
  38. package/types/commands/ChatMessage.d.ts +36 -0
  39. package/types/commands/CloseCommand.d.ts +25 -0
  40. package/types/commands/Command.d.ts +28 -0
  41. package/types/commands/DepsCommand.d.ts +32 -0
  42. package/types/commands/DepsMessage.d.ts +28 -0
  43. package/types/commands/HostCommand.d.ts +7 -7
  44. package/types/commands/HostMessage.d.ts +44 -0
  45. package/types/commands/InitCommand.d.ts +24 -19
  46. package/types/commands/InitMessage.d.ts +24 -0
  47. package/types/commands/ListCommand.d.ts +8 -8
  48. package/types/commands/ListMessage.d.ts +30 -0
  49. package/types/commands/PublishCommand.d.ts +36 -47
  50. package/types/commands/PublishMessage.d.ts +39 -0
  51. package/types/commands/SealCommand.d.ts +7 -7
  52. package/types/commands/SealMessage.d.ts +28 -0
  53. package/types/commands/ServeCommand.d.ts +7 -7
  54. package/types/commands/ServeMessage.d.ts +30 -0
  55. package/types/commands/ShowCommand.d.ts +7 -7
  56. package/types/commands/SpecCommand.d.ts +25 -0
  57. package/types/commands/StatusCommand.d.ts +9 -0
  58. package/types/commands/SubCommand.d.ts +9 -7
  59. package/types/commands/ValidateCommand.d.ts +7 -7
  60. package/types/commands/ValidateMessage.d.ts +20 -0
  61. package/types/db/ReleaseDB.d.ts +2 -2
  62. package/types/index.d.ts +10 -0
  63. package/types/ui/cli/ReleaseCLi.d.ts +10 -0
  64. package/types/ui/cli/index.d.ts +0 -0
  65. package/types/utils/runSpawn.d.ts +0 -0
  66. package/.datasets/README.dataset.jsonl +0 -19
  67. package/.editorconfig +0 -20
  68. package/CHANGELOG.md +0 -6
  69. package/CONTRIBUTING.md +0 -42
  70. package/bin/commands/InitCommand.js +0 -10
  71. package/bin/lib/functions.js +0 -240
  72. package/bin/lib/functions.test.js +0 -138
  73. package/bin/release.js +0 -12
  74. package/bin/release.test.js +0 -73
  75. package/bin/temp/package.json +0 -1
  76. package/docs/system.uk.md +0 -413
  77. package/docs/uk/README.md +0 -284
  78. package/legacy/PrepareCommand.js +0 -154
  79. package/legacy/PrepareCommand.test.js +0 -189
  80. package/legacy/changelog.js +0 -446
  81. package/releases/1/0/v1.0.0/models/Person.js +0 -26
  82. package/releases/1/0/v1.0.0/models/Person.test.js +0 -44
  83. package/releases/1/0/v1.0.0/models/company.js +0 -11
  84. package/releases/1/0/v1.0.0/models/members.js +0 -14
  85. package/releases/1/0/v1.0.0/models/members.test.js +0 -21
  86. package/releases/1/0/v1.0.0/models/teams/DevTeam.js +0 -6
  87. package/releases/1/0/v1.0.0/models/teams/DevTeam.test.js +0 -15
  88. package/releases/1/0/v1.0.0/models/teams/index.js +0 -5
  89. package/releases/1/0/v1.0.0/release.js +0 -12
  90. package/releases/1/0/v1.0.0/release.md +0 -121
  91. package/releases/1/0/v1.0.0/release.test.js +0 -36
  92. package/releases/1/0/v1.0.0/test/YaRaSLove/git-native.test.js +0 -14
  93. package/releases/current.js +0 -3
  94. package/releases/index.js +0 -11
  95. package/src/Release/MarkdownToTest.test.js +0 -60
  96. package/src/ReleaseCLI.js +0 -122
  97. package/src/architecture/ProjectManagementAsCode.test.js +0 -128
  98. package/src/co/AppCommandMessage.js +0 -22
  99. package/src/commands/ChatCommand.js +0 -56
  100. package/src/commands/HostCommand.js +0 -63
  101. package/src/commands/ListCommand.js +0 -56
  102. package/src/commands/SealCommand.js +0 -51
  103. package/src/commands/ServeCommand.js +0 -53
  104. package/src/commands/ShowCommand.js +0 -58
  105. package/src/commands/SubCommand.js +0 -21
  106. package/src/commands/ValidateCommand.js +0 -66
  107. package/system.md +0 -571
  108. package/tsconfig.json +0 -23
  109. /package/{bin/lib/temp/pnpm-lock.yaml → src/ui/cli/index.js} +0 -0
@@ -1,121 +0,0 @@
1
- # v1.0.0 - 2025-08-18
2
-
3
- ## PM as a code
4
-
5
- Project management as a code is real if you believe and do this way.
6
-
7
- ### Team
8
-
9
- #### developer
10
-
11
- - YaRaSLove (ЯRаСлав) <support@yaro.page>
12
-
13
- #### assistant
14
-
15
- - Qwen-3-coder-480b <https://api.cerebras.ai/>
16
-
17
- ## CLI application
18
-
19
- ### 1. Git-Native & Version-Controlled Workflow [](./task/git-native.test.js)
20
-
21
- - Treats project management like code: everything is versioned, auditable, reversible.
22
- - No external tools or databases needed — just Git.
23
- - Natural for developers; lowers barrier to entry for dev-centric teams.
24
-
25
- #### Acceptance Criteria
26
-
27
- - MUST verify that `git status` does not show any `releases/0/0/v0.0.0/*` files;
28
- - MUST create all required files according to the structure:
29
- ```text
30
- release.md ← Release notes (tasks, sections)
31
- release.js ← Team, roles, config (class Release)
32
- release.test.js ← Main test suite (CI entrypoint)
33
- test/
34
- {username}/ ← Optional scope
35
- tasks.test.js ← User-specific tests
36
- chat/ ← Release-wide chat
37
- 2025/08/18/
38
- 162000420.{username}.md ← Message (timestamp + user)
39
- task/
40
- ux.logo/ ← Task by slug
41
- config.json ← { assignee, priority, dependsOn }
42
- approved/
43
- ceo.json ← Approval record (GPG-signed)
44
- chat/
45
- 2025/08/18/
46
- 162000420.{username}.md ← Task-specific message
47
- assets/ ← Designs, docs, deliverables
48
- retro.md ← Final reflection (sealed)
49
- .cache/ ← gitignored: read state, session
50
- ```
51
- - MUST confirm with `git status` which files are ignored and which are tracked.
52
-
53
- ---
54
-
55
- ### 2. Test-Driven Project Progress [](./test/YaRaSLove/test-driven.test.js)
56
- - Tasks become testable units → Pass/fail defines completion.
57
- - Automates status tracking via CI/CD: "Done" = test passes.
58
- - Enables **automated QA gates in releases** — powerful for regulated environments.
59
-
60
- #### Acceptance Criteria
61
- - MUST write `release.md` inside the test folder `releases/0/0/v0.0.0/release.md` using proper formatting;
62
- - MUST initialize the release with `nan0release init v0.0.0`;
63
- - MUST check the generated tests and compare them against expected templates;
64
- - MUST prepare tests generated from complete releases notes markdown file with command `nan0release prepare v0.0.0`
65
- - MUST begin the workflow: all tests start in `todo`, transition states by modifying files and executing tests, validate each state (`todo`, `in progress`, `complete`, `approved`), verify release status and confirm `git tag` commands are executed once all tasks complete.
66
- - MUST execute `nan0release [status] v0.0.0`
67
- - MUST execute `nan0release todo v0.0.0`
68
- - MUST execute `nan0release validate v0.0.0`
69
- - MUST execute `nan0release seal v0.0.0`
70
-
71
- ### 3. Transparent & Predictable Progress Tracking [](./test/YaRaSLove/progress-tracking.test.js)
72
- - Real-time progress %, ETA, and velocity derived from commit history.
73
- - Immediate visibility through stats (pending/pass/fail).
74
- - Eliminates manual updates in Jira/Trello → reduces noise and inaccuracies.
75
-
76
- #### Acceptance Criteria
77
- - MUST validate updates occur upon commits (these may be simulated or run within an isolated temporary `.git` environment); ensure CI/CD generates reports which are both testable and consumable by UIs.
78
-
79
- ---
80
-
81
- ### 4. Integrated Team Communication [](./test/YaRaSLove/chat.test.js)
82
- - Structured chat per release and task embedded directly in file system.
83
- - Chronologically ordered, searchable, and version-controlled conversations.
84
- - `.cache` supports user-specific read/unread states without requiring a central server.
85
-
86
- #### Acceptance Criteria
87
-
88
- - MUST test by verifying created files after running the following commands:
89
- - `nan0release chat write "msg"`
90
- - `nan0release chat write ux.logo "help needed"`
91
- - `nan0release chat read`
92
- - `nan0release chat find "help"`
93
- - `nan0release chat --sender member@example.com`
94
- - `nan0release chat unread`
95
- - MUST test both output and resulting file content.
96
-
97
- ---
98
-
99
- ### 5. Self-Documenting Releases [](./test/YaRaSLove/docs.test.js)
100
-
101
- - Structured, machine-readable release notes, always current.
102
- - Markdown enhanced with YAML frontmatter enables metadata (assignees, dates, etc.).
103
- - Facilitates automatic generation of changelogs, roadmaps, dashboards.
104
-
105
- #### Acceptance Criteria
106
-
107
- - MUST validate success and failure outcomes of `nan0release validate` when processing valid and invalid release note formats. It must exit with status code 1 on a failure.
108
-
109
- ---
110
-
111
- ### 6. Extensible & Composable & Office-First & Decentralized by Design [ci-cd.test.js]
112
- - Assets such as designs, configurations, and logs stored within the release for full context.
113
- - Supports custom scripts, hooks, templates (e.g., `pre-release.js`, `post-release.sh`).
114
- - Seamlessly integrates into CI/CD pipelines.
115
- - Works offline and independently of external services.
116
- - No vendor lock-in — all data remains in the repository.
117
-
118
- #### Acceptance Criteria
119
-
120
- - MUST confirm context preservation by adding test files to assets and validating output of `git status`.
121
-
@@ -1,36 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
-
4
- import Release from "./release.js"
5
-
6
- /**
7
- * @todo
8
- * It must validate the structure of every release.
9
- * It must be in src/Release.validate().
10
- * It must validate loaded markdown into document with the file structure
11
- */
12
-
13
- describe("Release v1.0.0", () => {
14
- it("has a valid static version", () => {
15
- assert.equal(Release.version, "v1.0.0")
16
- })
17
-
18
- it("has a valid static date", () => {
19
- assert.equal(Release.createdAt, "2025-08-18")
20
- })
21
-
22
- it("references a valid Company with name", () => {
23
- assert.ok(Release.company)
24
- assert.equal(Release.company.name, "NaN•Web")
25
- })
26
-
27
- it("has a DevTeam defined in teams", () => {
28
- assert.ok(Release.teams.DevTeam)
29
- assert.equal(Release.teams.DevTeam.name, "Software development team")
30
- })
31
-
32
- it("includes all core models: Person, Contact, Message", () => {
33
- assert.ok(typeof Person !== 'undefined' || typeof Release.members.YaRaSLove !== 'undefined')
34
- // Note: these would be imported if external deps existed
35
- })
36
- })
@@ -1,14 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import assert from "node:assert/strict"
3
-
4
- describe("Git-Native & Version-Controlled Workflow", () => {
5
- it("Treats project management like code: everything is versioned, auditable, reversible.", () => {
6
-
7
- })
8
- it("No external tools or databases needed — just Git.", () => {
9
-
10
- })
11
- it("Natural for developers; lowers barrier to entry for dev-centric teams.", () => {
12
-
13
- })
14
- })
@@ -1,3 +0,0 @@
1
- import v1_0_0 from "./1/0/v1.0.0/release.js"
2
-
3
- export default v1_0_0
package/releases/index.js DELETED
@@ -1,11 +0,0 @@
1
- import v1_0_0 from "./1/0/v1.0.0/release.js"
2
- import Release from "../src/Release.js"
3
-
4
- /**
5
- * @type {Record<string, Release>}
6
- */
7
- const releases = {
8
- v1_0_0
9
- }
10
-
11
- export default releases
@@ -1,60 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strictEqual } from "node:assert"
3
- import MarkdownToTest from "./MarkdownToTest.js"
4
-
5
- describe.skip("MarkdownToTest", () => {
6
- it("should generate node:test code from markdown release notes", () => {
7
- const parser = new MarkdownToTest()
8
- const markdown = `# v1.1.0 - 2025-08-19
9
-
10
- ## CLI application
11
- ### **Treats project management like code: everything is versioned, auditable, reversible.** [treats-project-management-like-code-everything-is-versioned-auditable-reversible]
12
- ### Draft **No external tools or databases needed — just Git.** [no-external-tools-or-databases-needed-just-git]
13
- ### **Natural for developers; lowers barrier to entry for dev-centric teams.** [natural-for-developers-lowers-barrier-to-entry-for-dev-centric-teams]
14
-
15
- ## UI application
16
- ### **Drag-and-drop interface** [drag-and-drop-interface]
17
- ### **Visual tree structure** [visual-tree-structure]`
18
-
19
- const expected = `import { describe, it } from "node:test"
20
- import { ok, strictEqual } from "node:assert"
21
-
22
- describe("v1.1.0 - 2025-08-19", () => {
23
- describe("CLI application", () => {
24
- it("Treats project management like code: everything is versioned, auditable, reversible.", () => {
25
- ok(true, "Task completed successfully")
26
- })
27
- it.skip("No external tools or databases needed — just Git.", () => {
28
- ok(false, "Draft task - not yet implemented")
29
- })
30
- it("Natural for developers; lowers barrier to entry for dev-centric teams.", () => {
31
- ok(true, "Task completed successfully")
32
- })
33
- })
34
- describe("UI application", () => {
35
- it("Drag-and-drop interface", () => {
36
- ok(true, "Task completed successfully")
37
- })
38
- it("Visual tree structure", () => {
39
- ok(true, "Task completed successfully")
40
- })
41
- })
42
- })`
43
-
44
- const result = parser.generateTests(markdown)
45
- strictEqual(result.trim(), expected.trim())
46
- })
47
-
48
- it("should throw error if markdown doesn't have version header", () => {
49
- const parser = new MarkdownToTest()
50
- const markdown = `## CLI application
51
- ### **Treats project management like code** [treats-project-management-like-code]`
52
-
53
- try {
54
- parser.generateTests(markdown)
55
- throw new Error("Should have thrown an error")
56
- } catch (error) {
57
- strictEqual(error.message, 'Markdown must start with version header: # vX.Y.Z - YYYY-MM-DD')
58
- }
59
- })
60
- })
package/src/ReleaseCLI.js DELETED
@@ -1,122 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from '@nan0web/co'
4
- import Logger from '@nan0web/log'
5
- import FS from "@nan0web/db-fs"
6
- import DB from "./db/ReleaseDB.js"
7
- import Release from './Release.js'
8
- import ChatCommand from './commands/ChatCommand.js'
9
- import HostCommand from './commands/HostCommand.js'
10
- import InitCommand from './commands/InitCommand.js'
11
- import ListCommand from './commands/ListCommand.js'
12
- import SealCommand from './commands/SealCommand.js'
13
- import ServeCommand from './commands/ServeCommand.js'
14
- import ShowCommand from './commands/ShowCommand.js'
15
- import AppCommandMessage from './co/AppCommandMessage.js'
16
-
17
- /**
18
- * CLI application for release management
19
- */
20
- export default class ReleaseCLI extends Command {
21
- static Message = AppCommandMessage
22
- /** @type {Logger} */
23
- logger
24
- /** @type {DB?} */
25
- db
26
- /** @type {FS} */
27
- fs
28
- /** @type {Record<string, Release>} */
29
- releases = {}
30
- /** @type {Release?} */
31
- current = null
32
-
33
- constructor() {
34
- super({
35
- name: 'release',
36
- help: 'Release management CLI tool'
37
- })
38
- this.logger = new Logger({ icons: true })
39
- this.fs = new FS()
40
- this.db = null
41
- this.addOption('releaseDir', String, '.', 'Release dir')
42
- this.addOption('version', String, '0.0.0', 'Release version')
43
- }
44
-
45
- /**
46
- * @param {string[] | string} argv
47
- * @returns {AppCommandMessage}
48
- */
49
- parse(argv = []) {
50
- return AppCommandMessage.from(super.parse(argv))
51
- }
52
-
53
- /**
54
- * Run CLI application
55
- * @param {string[]} argv - Command line arguments
56
- * @returns {Promise<void>}
57
- */
58
- async run(argv) {
59
- const msg = this.parse(argv)
60
- await this.fs.connect()
61
-
62
- const rootDb = this.fs.extract(/** @type {string} */(msg.opts.releaseDir))
63
-
64
- const indexStat = await rootDb.statDocument("index.js")
65
- const currentStat = await rootDb.statDocument("current.js")
66
- if (indexStat.exists && currentStat.exists) {
67
- const indexJs = rootDb.absolute("index.js")
68
- const currentJs = rootDb.absolute("current.js")
69
- try {
70
- this.releases = (await import(indexJs)).default
71
- this.current = (await import(currentJs)).default
72
- this.logger.info(Object.keys(this.releases).length, "release(s) found")
73
- } catch (e) {
74
- this.logger.error("Failed to load release database", e)
75
- this.current = null
76
- }
77
- } else {
78
- this.logger.info("No releases initialized. Use 'release init <version>' to create your first release.")
79
- }
80
-
81
- const globalOpts = { logger: this.logger }
82
- this.addSubcommand(new ListCommand(globalOpts))
83
- this.addSubcommand(new HostCommand(globalOpts))
84
- this.addSubcommand(new ServeCommand(globalOpts))
85
-
86
- if (this.current) {
87
- this.logger.info("Current release", this.current.version)
88
- this.db = DB.from(rootDb.extract(this.current.path))
89
- await this.db.connect()
90
- const document = await this.db.loadDocument("release.md")
91
- const release = new Release({ document, version: this.current.version })
92
- const currentOpts = { ...globalOpts, db: this.db }
93
- this.addSubcommand(new InitCommand(currentOpts))
94
- this.addSubcommand(new ShowCommand(currentOpts))
95
- this.addSubcommand(new ChatCommand(currentOpts))
96
- this.addSubcommand(new SealCommand(currentOpts))
97
- }
98
- else {
99
- this.logger.info("Provide a release version")
100
- this.logger.info(this.generateHelp())
101
- }
102
- return
103
- }
104
-
105
- async loadReleaseFiles() {
106
- if (!this.db) return
107
- const stream = this.db.findStream("")
108
- const started = Date.now()
109
- for await (const entry of stream) {
110
- const spent = ((Date.now() - started) / 1000).toFixed(3)
111
- this.logger.clearLine(this.logger.cursorUp())
112
- this.logger.info("Loading ..", spent + "s", this.db.data.size, entry.file.path)
113
- }
114
- const spent = ((Date.now() - started) / 1000).toFixed(3)
115
- this.logger.clearLine(this.logger.cursorUp(/** @type {DB} */(this.db).data.size & 1))
116
- const info = [
117
- ["Loaded in", spent + "s", /** @type {DB} */(this.db).data.size, "document(s)"],
118
- ["", "", /** @type {DB} */(this.db).releases.length, "release(s)"],
119
- ]
120
- this.logger.table(info, [], { padding: 1 })
121
- }
122
- }
@@ -1,128 +0,0 @@
1
- import { describe, it } from 'node:test'
2
- import { strict as assert } from 'node:assert'
3
- import {
4
- ProjectManagement,
5
- ReleaseManager,
6
- TaskTestSuite,
7
- ChangelogTaskManager
8
- } from './ProjectManagementAsCode.js'
9
-
10
- describe('ProjectManagementAsCode Architecture', () => {
11
- describe('ProjectManagement', () => {
12
- it('should initialize with default configuration', () => {
13
- const pm = new ProjectManagement()
14
- assert.equal(pm.projectPath, '.')
15
- assert.equal(pm.autoTrack, false)
16
- assert.ok(pm.tasks instanceof Map)
17
- })
18
-
19
- it('should register tasks with their test files', () => {
20
- const pm = new ProjectManagement()
21
- pm.registerTask('task-1', './tests/task1.test.js')
22
- pm.registerTask('task-2', './tests/task2.test.js')
23
-
24
- assert.equal(pm.tasks.size, 2)
25
- assert.equal(pm.tasks.get('task-1'), './tests/task1.test.js')
26
- assert.equal(pm.tasks.get('task-2'), './tests/task2.test.js')
27
- })
28
-
29
- it('should validate project state by running task tests', async () => {
30
- const pm = new ProjectManagement()
31
- pm.registerTask('task-1', './tests/task1.test.js')
32
- pm.registerTask('task-2', './tests/task2.test.js')
33
-
34
- const results = await pm.validateProjectState()
35
- assert.ok(results.passed.length >= 0)
36
- assert.ok(results.failed.length >= 0)
37
- assert.ok(results.pending.length >= 0)
38
- })
39
- })
40
-
41
- describe('ReleaseManager', () => {
42
- it('should initialize with project manager', () => {
43
- const pm = new ProjectManagement()
44
- const rm = new ReleaseManager(pm)
45
-
46
- assert.equal(rm.projectManager, pm)
47
- })
48
-
49
- it.skip('should validate release with version calculation', async () => {
50
- const pm = new ProjectManagement()
51
- const rm = new ReleaseManager(pm)
52
-
53
- const validation = await rm.validateRelease('1.0.0')
54
- assert.equal(validation.version, '1.0.0')
55
- assert.ok(validation.timestamp)
56
- assert.ok(validation.tasks.total >= 0)
57
- })
58
-
59
- it('should execute release process with validation', async () => {
60
- const pm = new ProjectManagement()
61
- const rm = new ReleaseManager(pm)
62
-
63
- const result = await rm.executeRelease('patch')
64
- assert.ok(result.version)
65
- assert.ok(result.validation)
66
- assert.equal(typeof result.published, 'boolean')
67
- })
68
-
69
- it('should block release when tests fail and ignore option is not set', async () => {
70
- const pm = new ProjectManagement()
71
- const rm = new ReleaseManager(pm)
72
-
73
- // Mock validation to simulate failed tasks
74
- rm.projectManager.validateProjectState = async () => ({
75
- passed: [],
76
- failed: ['task-1'],
77
- pending: []
78
- })
79
-
80
- try {
81
- await rm.executeRelease('patch')
82
- assert.fail('Should have thrown an error')
83
- } catch (error) {
84
- assert.ok(error.message.includes('Release blocked'))
85
- }
86
- })
87
- })
88
-
89
- describe('TaskTestSuite', () => {
90
- it.skip('should extend TestSuite with task metadata', () => {
91
- const testFunction = () => { }
92
- const taskSuite = new TaskTestSuite('task-1', 'Test task description', testFunction)
93
-
94
- assert.equal(taskSuite.taskId, 'task-1')
95
- assert.equal(taskSuite.description, 'Test task description')
96
- assert.equal(taskSuite.testFunction, testFunction)
97
- })
98
- })
99
-
100
- describe('ChangelogTaskManager', () => {
101
- it('should parse changelog content to extract tasks', () => {
102
- const ctm = new ChangelogTaskManager()
103
- const changelogContent = `
104
- # Changelog
105
-
106
- ## [1.0.0] - 2024-01-01
107
- ### Added
108
- - Initial release features
109
- - Core functionality implementation
110
- `
111
-
112
- const tasks = ctm.parseChangelog(changelogContent)
113
- assert.ok(Array.isArray(tasks))
114
- })
115
-
116
- it('should generate task tests from changelog entries', async () => {
117
- const ctm = new ChangelogTaskManager()
118
- const tasks = [
119
- { id: 'task-1', description: 'Implement feature A' },
120
- { id: 'task-2', description: 'Fix bug B' }
121
- ]
122
-
123
- // This should not throw an error
124
- await ctm.generateTaskTests(tasks)
125
- assert.ok(true)
126
- })
127
- })
128
- })
@@ -1,22 +0,0 @@
1
- import { CommandMessage } from "@nan0web/co"
2
- import AppCommandOptions from "./AppCommandOptions.js"
3
-
4
- export default class AppCommandMessage extends CommandMessage {
5
- /** @returns {AppCommandOptions} */
6
- get opts() {
7
- return /** @type {AppCommandOptions} */ (super.opts)
8
- }
9
-
10
- set opts(value) {
11
- super.opts = value
12
- }
13
-
14
- /**
15
- * @param {*} input
16
- * @returns {AppCommandMessage}
17
- */
18
- static from(input) {
19
- if (input instanceof AppCommandMessage) return input
20
- return new AppCommandMessage(input)
21
- }
22
- }
@@ -1,56 +0,0 @@
1
- import SubCommand from "./SubCommand.js"
2
- import { appendFile, readFile } from "node:fs/promises"
3
- import { join } from "node:path"
4
- import AppCommandMessage from "../co/AppCommandMessage.js"
5
-
6
- /**
7
- * ChatCommand – writes a message to the release‑wide chat (chat/…/timestamp.username.md)
8
- *
9
- * Usage:
10
- * release chat write "your message"
11
- *
12
- * Options:
13
- * --user <string> – username to embed in the filename (defaults to "anonymous")
14
- */
15
- class ChatCommand extends SubCommand {
16
- constructor(options = {}) {
17
- super(options)
18
- this.addOption("user", String, "anonymous", "Chat username")
19
- this.addOption("write", Boolean, false, "Write a new chat line")
20
- }
21
-
22
- /**
23
- * Run the chat command.
24
- *
25
- * @param {AppCommandMessage} ctx – command context (contains args and opts)
26
- */
27
- async run(ctx) {
28
- if (!ctx.opts.write) {
29
- this.logger.error("No action specified – use --write to add a message")
30
- return
31
- }
32
- if (!this.db) {
33
- this.logger.error("No DB – cannot locate chat folder")
34
- return
35
- }
36
-
37
- const message = ctx.args.join(" ")
38
- if (!message) {
39
- this.logger.error("Empty message – nothing to write")
40
- return
41
- }
42
-
43
- const now = Date.now()
44
- const username = /** @type {string} */(ctx.opts.user)
45
- const chatPath = join(this.db.cwd, "chat", "2025", "08", `${now}.${username}.md`)
46
-
47
- // ensure directory exists
48
- const dir = chatPath.split("/").slice(0, -1).join("/")
49
- await import("node:fs/promises").then(m => m.mkdir(dir, { recursive: true }))
50
-
51
- await appendFile(chatPath, message + "\n", "utf8")
52
- this.logger.success(`Message written to ${chatPath}`)
53
- }
54
- }
55
-
56
- export default ChatCommand
@@ -1,63 +0,0 @@
1
- import SubCommand from "./SubCommand.js"
2
- import { createServer } from "node:http"
3
- import { readFile } from "node:fs/promises"
4
- import { join } from "node:path"
5
-
6
- /**
7
- * HostCommand – starts a minimal HTTP server that serves the current
8
- * release markdown and a tiny JSON status endpoint.
9
- *
10
- * Usage:
11
- * release host [--port <number>] [--webui]
12
- */
13
- export default class HostCommand extends SubCommand {
14
- constructor(options = {}) {
15
- super(options)
16
- this.addOption("port", Number, 3000, "Port for the HTTP server")
17
- this.addOption("webui", Boolean, false, "Serve a simple web UI")
18
- }
19
-
20
- /**
21
- * Run the host server.
22
- *
23
- * @param {Object} ctx – command context
24
- */
25
- async run(ctx) {
26
- const port = ctx.opts.port
27
- const releasePath = this.db?.cwd
28
- if (!releasePath) {
29
- this.logger.error("No release directory (db.cwd) – cannot host")
30
- return
31
- }
32
-
33
- const mdPath = join(releasePath, "release.md")
34
-
35
- const server = createServer(async (req, res) => {
36
- if (req.url === "/") {
37
- // simple UI – just render markdown as preformatted text
38
- try {
39
- const md = await readFile(mdPath, "utf8")
40
- const body = ctx.opts.webui
41
- ? `<html><body><pre>${md}</pre></body></html>`
42
- : md
43
- res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" })
44
- res.end(body)
45
- } catch (e) {
46
- res.writeHead(404)
47
- res.end("release.md not found")
48
- }
49
- } else if (req.url === "/status") {
50
- res.writeHead(200, { "Content-Type": "application/json" })
51
- res.end(JSON.stringify({ version: ctx.opts.version || "unknown" }))
52
- } else {
53
- res.writeHead(404)
54
- res.end("Not found")
55
- }
56
- })
57
-
58
- server.listen(port, () => {
59
- this.logger.success(`Release hosted at http://localhost:${port}`)
60
- if (ctx.opts.webui) this.logger.info("Web UI enabled")
61
- })
62
- }
63
- }